diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 0000000000..20ee6fe8f3 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,6491 @@ +{ + "projectName": "pmd", + "projectOwner": "pmd", + "repoType": "github", + "repoHost": "https://github.com", + "files": [ + "docs/pages/pmd/projectdocs/credits.md" + ], + "imageSize": 100, + "commit": true, + "commitConvention": "none", + "contributors": [ + { + "login": "adangel", + "name": "Andreas Dangel", + "avatar_url": "https://avatars.githubusercontent.com/u/1573684?v=4", + "profile": "https://github.com/adangel", + "contributions": [ + "code", + "doc", + "bug", + "maintenance" + ] + }, + { + "login": "oowekyala", + "name": "Clément Fournier", + "avatar_url": "https://avatars.githubusercontent.com/u/24524930?v=4", + "profile": "https://github.com/oowekyala", + "contributions": [ + "code", + "doc", + "bug", + "maintenance" + ] + }, + { + "login": "jsotuyod", + "name": "Juan Martín Sotuyo Dodero", + "avatar_url": "https://avatars.githubusercontent.com/u/802626?v=4", + "profile": "https://github.com/jsotuyod", + "contributions": [ + "code", + "doc", + "bug", + "maintenance" + ] + }, + { + "login": "sergeygorbaty", + "name": "sergeygorbaty", + "avatar_url": "https://avatars.githubusercontent.com/u/14813710?v=4", + "profile": "https://github.com/sergeygorbaty", + "contributions": [ + "code" + ] + }, + { + "login": "sturton", + "name": "sturton", + "avatar_url": "https://avatars.githubusercontent.com/u/1734891?v=4", + "profile": "https://github.com/sturton", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "rsoesemann", + "name": "Robert Sösemann", + "avatar_url": "https://avatars.githubusercontent.com/u/8180281?v=4", + "profile": "https://github.com/rsoesemann", + "contributions": [ + "code", + "doc", + "talk", + "bug" + ] + }, + { + "login": "maikelsteneker", + "name": "Maikel Steneker", + "avatar_url": "https://avatars.githubusercontent.com/u/2788927?v=4", + "profile": "https://github.com/maikelsteneker", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "gwilymatgearset", + "name": "gwilymatgearset", + "avatar_url": "https://avatars.githubusercontent.com/u/43957113?v=4", + "profile": "https://github.com/gwilymatgearset", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "akshatbahety", + "name": "Akshat Bahety", + "avatar_url": "https://avatars.githubusercontent.com/u/17676203?v=4", + "profile": "https://github.com/akshatbahety", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "JosephAllen", + "name": "Joseph", + "avatar_url": "https://avatars.githubusercontent.com/u/3989748?v=4", + "profile": "https://www.linkedin.com/in/joseph-allen-9602671/", + "contributions": [ + "code" + ] + }, + { + "login": "DavidRenz", + "name": "David Renz", + "avatar_url": "https://avatars.githubusercontent.com/u/8180433?v=4", + "profile": "https://github.com/DavidRenz", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "rpelisse", + "name": "Pelisse Romain", + "avatar_url": "https://avatars.githubusercontent.com/u/117836?v=4", + "profile": "http://belaran.eu/wordpress/", + "contributions": [ + "code", + "doc", + "bug" + ] + }, + { + "login": "Drofff", + "name": "Mykhailo Palahuta", + "avatar_url": "https://avatars.githubusercontent.com/u/45700628?v=4", + "profile": "https://github.com/Drofff", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "gibarsin", + "name": "Gonzalo Exequiel Ibars Ingman", + "avatar_url": "https://avatars.githubusercontent.com/u/9052089?v=4", + "profile": "https://github.com/gibarsin", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "djydewang", + "name": "BBG", + "avatar_url": "https://avatars.githubusercontent.com/u/18324858?v=4", + "profile": "https://github.com/djydewang", + "contributions": [ + "code", + "doc", + "bug" + ] + }, + { + "login": "XenoAmess", + "name": "XenoAmess", + "avatar_url": "https://avatars.githubusercontent.com/u/17455337?v=4", + "profile": "http://xenoamess.com/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "borovikovd", + "name": "Denis Borovikov", + "avatar_url": "https://avatars.githubusercontent.com/u/43751473?v=4", + "profile": "https://github.com/borovikovd", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "jeffhube", + "name": "Jeff Hube", + "avatar_url": "https://avatars.githubusercontent.com/u/1283264?v=4", + "profile": "https://github.com/jeffhube", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "piotrszymanski-sc", + "name": "piotrszymanski-sc", + "avatar_url": "https://avatars.githubusercontent.com/u/71124942?v=4", + "profile": "https://github.com/piotrszymanski-sc", + "contributions": [ + "code" + ] + }, + { + "login": "kris-scheibe", + "name": "Kris Scheibe", + "avatar_url": "https://avatars.githubusercontent.com/u/20039785?v=4", + "profile": "https://github.com/kris-scheibe", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "jonathanwiesel", + "name": "Jonathan Wiesel", + "avatar_url": "https://avatars.githubusercontent.com/u/1326781?v=4", + "profile": "https://github.com/jonathanwiesel", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "prophet1906", + "name": "Shubham", + "avatar_url": "https://avatars.githubusercontent.com/u/32415088?v=4", + "profile": "https://github.com/prophet1906", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "JAertgeerts", + "name": "Jan Aertgeerts", + "avatar_url": "https://avatars.githubusercontent.com/u/2192516?v=4", + "profile": "https://www.linkedin.com/in/janaertgeerts/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "pyxide", + "name": "Olivier Parent", + "avatar_url": "https://avatars.githubusercontent.com/u/9992381?v=4", + "profile": "https://github.com/pyxide", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "jbartolotta-sfdc", + "name": "Jeff Bartolotta", + "avatar_url": "https://avatars.githubusercontent.com/u/18196574?v=4", + "profile": "https://github.com/jbartolotta-sfdc", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "pmd-bot", + "name": "Machine account for PMD", + "avatar_url": "https://avatars.githubusercontent.com/u/26070915?v=4", + "profile": "https://pmd.github.io/", + "contributions": [ + "code" + ] + }, + { + "login": "matifraga", + "name": "Matías Fraga", + "avatar_url": "https://avatars.githubusercontent.com/u/7543268?v=4", + "profile": "https://github.com/matifraga", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "kabroxiko", + "name": "kabroxiko", + "avatar_url": "https://avatars.githubusercontent.com/u/20568120?v=4", + "profile": "https://github.com/kabroxiko", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "IDoCodingStuffs", + "name": "IDoCodingStuffs", + "avatar_url": "https://avatars.githubusercontent.com/u/43346404?v=4", + "profile": "https://github.com/IDoCodingStuffs", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "albfernandez", + "name": "Alberto Fernández", + "avatar_url": "https://avatars.githubusercontent.com/u/2701620?v=4", + "profile": "https://github.com/albfernandez", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "ajeans", + "name": "Arnaud Jeansen", + "avatar_url": "https://avatars.githubusercontent.com/u/2376384?v=4", + "profile": "https://github.com/ajeans", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "mmoyaferrer", + "name": "Manuel Moya Ferrer", + "avatar_url": "https://avatars.githubusercontent.com/u/15876612?v=4", + "profile": "https://www.linkedin.com/in/manuel-moya-ferrer-11163168/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "Thunderforge", + "name": "Thunderforge", + "avatar_url": "https://avatars.githubusercontent.com/u/6200170?v=4", + "profile": "https://github.com/Thunderforge", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "rajeshggwp", + "name": "RajeshR", + "avatar_url": "https://avatars.githubusercontent.com/u/8025160?v=4", + "profile": "https://github.com/rajeshggwp", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "tomidelucca", + "name": "Tomi De Lucca", + "avatar_url": "https://avatars.githubusercontent.com/u/1288160?v=4", + "profile": "https://github.com/tomidelucca", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "YodaDaCoda", + "name": "William Brockhus", + "avatar_url": "https://avatars.githubusercontent.com/u/365349?v=4", + "profile": "https://github.com/YodaDaCoda", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "Snap252", + "name": "Thomas Smith", + "avatar_url": "https://avatars.githubusercontent.com/u/10380619?v=4", + "profile": "https://github.com/Snap252", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "KroArtem", + "name": "Artem", + "avatar_url": "https://avatars.githubusercontent.com/u/1813101?v=4", + "profile": "https://kroartem.wordpress.com/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "pamidi99", + "name": "Bhanu Prakash Pamidi", + "avatar_url": "https://avatars.githubusercontent.com/u/16791958?v=4", + "profile": "https://github.com/pamidi99", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "DTecheira", + "name": "Techeira Damián", + "avatar_url": "https://avatars.githubusercontent.com/u/1074288?v=4", + "profile": "https://github.com/DTecheira", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "renatoliveira", + "name": "Renato Oliveira", + "avatar_url": "https://avatars.githubusercontent.com/u/6956403?v=4", + "profile": "https://dogeforce.com/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "anand13s", + "name": "Anand Subramanian", + "avatar_url": "https://avatars.githubusercontent.com/u/3236002?v=4", + "profile": "https://github.com/anand13s", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "ozangulle", + "name": "Ozan Gulle", + "avatar_url": "https://avatars.githubusercontent.com/u/1334150?v=4", + "profile": "https://github.com/ozangulle", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "refactormyself", + "name": "Bolarinwa Saheed Olayemi", + "avatar_url": "https://avatars.githubusercontent.com/u/17991837?v=4", + "profile": "https://github.com/refactormyself", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "daleanson", + "name": "Dale", + "avatar_url": "https://avatars.githubusercontent.com/u/2112276?v=4", + "profile": "https://github.com/daleanson", + "contributions": [ + "code" + ] + }, + { + "login": "davidburstromspotify", + "name": "David Burström", + "avatar_url": "https://avatars.githubusercontent.com/u/2573207?v=4", + "profile": "https://github.com/davidburstromspotify", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "MatiasComercio", + "name": "MatiasComercio", + "avatar_url": "https://avatars.githubusercontent.com/u/9677633?v=4", + "profile": "https://github.com/MatiasComercio", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "pzygielo", + "name": "Piotrek Żygieło", + "avatar_url": "https://avatars.githubusercontent.com/u/11896137?v=4", + "profile": "https://github.com/pzygielo", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "tweimer", + "name": "Tobias Weimer", + "avatar_url": "https://avatars.githubusercontent.com/u/2698843?v=4", + "profile": "https://miranda-ng.org/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "testation21", + "name": "testation21", + "avatar_url": "https://avatars.githubusercontent.com/u/47239708?v=4", + "profile": "https://github.com/testation21", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "rmohan20", + "name": "rmohan20", + "avatar_url": "https://avatars.githubusercontent.com/u/58573547?v=4", + "profile": "https://github.com/rmohan20", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "hvbtup", + "name": "hvbtup", + "avatar_url": "https://avatars.githubusercontent.com/u/7644776?v=4", + "profile": "https://github.com/hvbtup", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "andrey81inmd", + "name": "andrey81inmd", + "avatar_url": "https://avatars.githubusercontent.com/u/2624682?v=4", + "profile": "https://github.com/andrey81inmd", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "jtjeferreira", + "name": "João Ferreira", + "avatar_url": "https://avatars.githubusercontent.com/u/943051?v=4", + "profile": "https://github.com/jtjeferreira", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "clem0110", + "name": "Kirk Clemens", + "avatar_url": "https://avatars.githubusercontent.com/u/7726426?v=4", + "profile": "https://github.com/clem0110", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "larrydiamond", + "name": "Larry Diamond", + "avatar_url": "https://avatars.githubusercontent.com/u/1066589?v=4", + "profile": "https://www.linkedin.com/in/larry-diamond-3964042/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "hvbargen", + "name": "Henning von Bargen", + "avatar_url": "https://avatars.githubusercontent.com/u/37015738?v=4", + "profile": "https://github.com/hvbargen", + "contributions": [ + "code" + ] + }, + { + "login": "GuntherSchrijvers", + "name": "Gunther Schrijvers", + "avatar_url": "https://avatars.githubusercontent.com/u/56870283?v=4", + "profile": "https://github.com/GuntherSchrijvers", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "kenji21", + "name": "kenji21", + "avatar_url": "https://avatars.githubusercontent.com/u/1105089?v=4", + "profile": "https://github.com/kenji21", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "vovkss", + "name": "Alex Shesterov", + "avatar_url": "https://avatars.githubusercontent.com/u/5391412?v=4", + "profile": "https://github.com/vovkss", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "jfeingold35", + "name": "Josh Feingold", + "avatar_url": "https://avatars.githubusercontent.com/u/4054488?v=4", + "profile": "https://github.com/jfeingold35", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "josemanuelrolon", + "name": "josemanuelrolon", + "avatar_url": "https://avatars.githubusercontent.com/u/1685807?v=4", + "profile": "https://github.com/josemanuelrolon", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "andipabst", + "name": "Andi Pabst", + "avatar_url": "https://avatars.githubusercontent.com/u/9639382?v=4", + "profile": "https://github.com/andipabst", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "berkam", + "name": "berkam", + "avatar_url": "https://avatars.githubusercontent.com/u/26228441?v=4", + "profile": "https://github.com/berkam", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "lsoncini", + "name": "Lucas Soncini", + "avatar_url": "https://avatars.githubusercontent.com/u/12226579?v=4", + "profile": "https://github.com/lsoncini", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "xuthus", + "name": "Sergey Yanzin", + "avatar_url": "https://avatars.githubusercontent.com/u/6282044?v=4", + "profile": "https://github.com/xuthus", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "ollieabbey", + "name": "Ollie Abbey", + "avatar_url": "https://avatars.githubusercontent.com/u/52665918?v=4", + "profile": "https://github.com/ollieabbey", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "hooperbloob", + "name": "Hooperbloob", + "avatar_url": "https://avatars.githubusercontent.com/u/1541370?v=4", + "profile": "https://github.com/hooperbloob", + "contributions": [ + "code" + ] + }, + { + "login": "0xflotus", + "name": "0xflotus", + "avatar_url": "https://avatars.githubusercontent.com/u/26602940?v=4", + "profile": "https://github.com/0xflotus", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "atrosinenko", + "name": "Anatoly Trosinenko", + "avatar_url": "https://avatars.githubusercontent.com/u/9654772?v=4", + "profile": "https://atrosinenko.github.io/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "Vampire", + "name": "Björn Kautler", + "avatar_url": "https://avatars.githubusercontent.com/u/325196?v=4", + "profile": "https://github.com/Vampire", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "fjalvingh", + "name": "Frits Jalvingh", + "avatar_url": "https://avatars.githubusercontent.com/u/1500452?v=4", + "profile": "http://domui.org/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "John-Teng", + "name": "John-Teng", + "avatar_url": "https://avatars.githubusercontent.com/u/16723151?v=4", + "profile": "https://github.com/John-Teng", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "acanda", + "name": "Philip Graf", + "avatar_url": "https://avatars.githubusercontent.com/u/174978?v=4", + "profile": "https://github.com/acanda", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "YYoungC", + "name": "Young Chan", + "avatar_url": "https://avatars.githubusercontent.com/u/55069165?v=4", + "profile": "https://github.com/YYoungC", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "marob", + "name": "Maxime Robert", + "avatar_url": "https://avatars.githubusercontent.com/u/3486231?v=4", + "profile": "https://github.com/marob", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "rsalvador", + "name": "Roman Salvador", + "avatar_url": "https://avatars.githubusercontent.com/u/1301827?v=4", + "profile": "https://github.com/rsalvador", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "ryan-gustafson", + "name": "ryan-gustafson", + "avatar_url": "https://avatars.githubusercontent.com/u/1227016?v=4", + "profile": "https://github.com/ryan-gustafson", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "chrisdutz", + "name": "Christofer Dutz", + "avatar_url": "https://avatars.githubusercontent.com/u/651105?v=4", + "profile": "https://github.com/chrisdutz", + "contributions": [ + "code" + ] + }, + { + "login": "xnYi9wRezm", + "name": "xnYi9wRezm", + "avatar_url": "https://avatars.githubusercontent.com/u/61201892?v=4", + "profile": "https://github.com/xnYi9wRezm", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "darakian", + "name": "Jon Moroney", + "avatar_url": "https://avatars.githubusercontent.com/u/3607524?v=4", + "profile": "https://darakian.github.io/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "pchittum", + "name": "Peter Chittum", + "avatar_url": "https://avatars.githubusercontent.com/u/1127876?v=4", + "profile": "https://github.com/pchittum", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "rmartinus", + "name": "Robbie Martinus", + "avatar_url": "https://avatars.githubusercontent.com/u/12573669?v=4", + "profile": "https://github.com/rmartinus", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "robinst", + "name": "Robin Stocker", + "avatar_url": "https://avatars.githubusercontent.com/u/16778?v=4", + "profile": "https://www.whatsthistimestamp.com/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "utkuc", + "name": "Utku Cuhadaroglu", + "avatar_url": "https://avatars.githubusercontent.com/u/15714598?v=4", + "profile": "https://github.com/utkuc", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "dependabot[bot]", + "name": "dependabot[bot]", + "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", + "profile": "https://github.com/apps/dependabot", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "epidemia", + "name": "Andrey Mochalov", + "avatar_url": "https://avatars.githubusercontent.com/u/3083503?v=4", + "profile": "https://www.linkedin.com/in/andrey-mochalov-063751108/?locale=en_US", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "orimarko", + "name": "orimarko", + "avatar_url": "https://avatars.githubusercontent.com/u/17137249?v=4", + "profile": "https://github.com/orimarko", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "zgrzyt93", + "name": "zgrzyt93", + "avatar_url": "https://avatars.githubusercontent.com/u/54275965?v=4", + "profile": "https://github.com/zgrzyt93", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "dionisioC", + "name": "Dionisio Cortés Fernández", + "avatar_url": "https://avatars.githubusercontent.com/u/8872359?v=4", + "profile": "https://github.com/dionisioC", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "markhall82", + "name": "Mark Hall", + "avatar_url": "https://avatars.githubusercontent.com/u/22261511?v=4", + "profile": "https://github.com/markhall82", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "hgschmie", + "name": "Henning Schmiedehausen", + "avatar_url": "https://avatars.githubusercontent.com/u/39495?v=4", + "profile": "http://about.me/hgschmie", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "reudismam", + "name": "reudismam", + "avatar_url": "https://avatars.githubusercontent.com/u/1970407?v=4", + "profile": "https://github.com/reudismam", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "dreniers", + "name": "Dennie Reniers", + "avatar_url": "https://avatars.githubusercontent.com/u/9007290?v=4", + "profile": "https://github.com/dreniers", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "astillich-igniti", + "name": "astillich-igniti", + "avatar_url": "https://avatars.githubusercontent.com/u/57359104?v=4", + "profile": "https://github.com/astillich-igniti", + "contributions": [ + "code" + ] + }, + { + "login": "tiobe", + "name": "TIOBE Software", + "avatar_url": "https://avatars.githubusercontent.com/u/2196103?v=4", + "profile": "http://www.tiobe.com/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "bric3", + "name": "Brice Dutheil", + "avatar_url": "https://avatars.githubusercontent.com/u/803621?v=4", + "profile": "https://blog.arkey.fr/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "Clint-Chester", + "name": "Clint Chester", + "avatar_url": "https://avatars.githubusercontent.com/u/12729644?v=4", + "profile": "https://github.com/Clint-Chester", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "CyrilSicard", + "name": "Cyril", + "avatar_url": "https://avatars.githubusercontent.com/u/45353161?v=4", + "profile": "https://github.com/CyrilSicard", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "filipesperandio", + "name": "Filipe Esperandio", + "avatar_url": "https://avatars.githubusercontent.com/u/316873?v=4", + "profile": "http://www.filipesperandio.com/", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "0cv", + "name": "Christophe Vidal", + "avatar_url": "https://avatars.githubusercontent.com/u/1253866?v=4", + "profile": "https://github.com/0cv", + "contributions": [ + "bug" + ] + }, + { + "login": "171563857", + "name": "Gol", + "avatar_url": "https://avatars.githubusercontent.com/u/22027896?v=4", + "profile": "https://github.com/171563857", + "contributions": [ + "bug" + ] + }, + { + "login": "1henni", + "name": "1henni", + "avatar_url": "https://avatars.githubusercontent.com/u/6975354?v=4", + "profile": "https://github.com/1henni", + "contributions": [ + "bug" + ] + }, + { + "login": "aaime", + "name": "Andrea Aime", + "avatar_url": "https://avatars.githubusercontent.com/u/325433?v=4", + "profile": "https://github.com/aaime", + "contributions": [ + "bug" + ] + }, + { + "login": "aaronhurst-google", + "name": "aaronhurst-google", + "avatar_url": "https://avatars.githubusercontent.com/u/86377278?v=4", + "profile": "https://github.com/aaronhurst-google", + "contributions": [ + "bug" + ] + }, + { + "login": "aaschmid", + "name": "Andreas Schmid", + "avatar_url": "https://avatars.githubusercontent.com/u/567653?v=4", + "profile": "https://github.com/aaschmid", + "contributions": [ + "bug" + ] + }, + { + "login": "Abhishek-kumar09", + "name": "Abhishek Kumar", + "avatar_url": "https://avatars.githubusercontent.com/u/48255244?v=4", + "profile": "https://www.linkedin.com/in/abhishek-kr09/", + "contributions": [ + "bug" + ] + }, + { + "login": "Adam-", + "name": "Adam", + "avatar_url": "https://avatars.githubusercontent.com/u/309739?v=4", + "profile": "https://github.com/Adam-", + "contributions": [ + "bug" + ] + }, + { + "login": "AdamCarroll", + "name": "Adam Carroll", + "avatar_url": "https://avatars.githubusercontent.com/u/15148135?v=4", + "profile": "https://vocabhunter.github.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "adamcin", + "name": "Mark Adamcin", + "avatar_url": "https://avatars.githubusercontent.com/u/524972?v=4", + "profile": "https://github.com/adamcin", + "contributions": [ + "bug" + ] + }, + { + "login": "aheber", + "name": "Heber", + "avatar_url": "https://avatars.githubusercontent.com/u/2173116?v=4", + "profile": "https://github.com/aheber", + "contributions": [ + "bug" + ] + }, + { + "login": "ahitrin", + "name": "Andrey Hitrin", + "avatar_url": "https://avatars.githubusercontent.com/u/587891?v=4", + "profile": "https://ahitrin.github.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "aidan-harding", + "name": "Aidan Harding", + "avatar_url": "https://avatars.githubusercontent.com/u/35935718?v=4", + "profile": "https://github.com/aidan-harding", + "contributions": [ + "bug" + ] + }, + { + "login": "akshayt23", + "name": "Akshay Thapa", + "avatar_url": "https://avatars.githubusercontent.com/u/8946657?v=4", + "profile": "https://github.com/akshayt23", + "contributions": [ + "bug" + ] + }, + { + "login": "alanbuttars", + "name": "Alan Buttars", + "avatar_url": "https://avatars.githubusercontent.com/u/10012095?v=4", + "profile": "https://alanbuttars.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "AlanHohn", + "name": "Alan Hohn", + "avatar_url": "https://avatars.githubusercontent.com/u/326739?v=4", + "profile": "http://blog.anvard.org/", + "contributions": [ + "bug" + ] + }, + { + "login": "alecharp", + "name": "Adrien Lecharpentier", + "avatar_url": "https://avatars.githubusercontent.com/u/985955?v=4", + "profile": "https://github.com/alecharp", + "contributions": [ + "bug" + ] + }, + { + "login": "alexmodis", + "name": "alexmodis", + "avatar_url": "https://avatars.githubusercontent.com/u/60091729?v=4", + "profile": "https://github.com/alexmodis", + "contributions": [ + "bug" + ] + }, + { + "login": "alixwar", + "name": "Alix", + "avatar_url": "https://avatars.githubusercontent.com/u/5415786?v=4", + "profile": "https://www.assaabloy.com/en/com/solutions/technology-platforms/", + "contributions": [ + "bug" + ] + }, + { + "login": "alwa", + "name": "Alix", + "avatar_url": "https://avatars.githubusercontent.com/u/1206247?v=4", + "profile": "https://github.com/alwa", + "contributions": [ + "bug" + ] + }, + { + "login": "amarkevich", + "name": "Alexey Markevich", + "avatar_url": "https://avatars.githubusercontent.com/u/62415?v=4", + "profile": "https://github.com/amarkevich", + "contributions": [ + "bug" + ] + }, + { + "login": "amitpd", + "name": "Amit Prasad", + "avatar_url": "https://avatars.githubusercontent.com/u/5464002?v=4", + "profile": "https://in.linkedin.com/in/amit-kumar-prasad-46300a26", + "contributions": [ + "bug" + ] + }, + { + "login": "anddero", + "name": "Karl-Andero Mere", + "avatar_url": "https://avatars.githubusercontent.com/u/9193126?v=4", + "profile": "https://github.com/anddero", + "contributions": [ + "bug" + ] + }, + { + "login": "andreasmarkussen", + "name": "Andreas Markussen", + "avatar_url": "https://avatars.githubusercontent.com/u/6911804?v=4", + "profile": "http://andreas.markussen.dk/", + "contributions": [ + "bug" + ] + }, + { + "login": "AndreasTu", + "name": "Andreas Turban", + "avatar_url": "https://avatars.githubusercontent.com/u/14334055?v=4", + "profile": "https://github.com/AndreasTu", + "contributions": [ + "bug" + ] + }, + { + "login": "andreoss", + "name": "andreoss", + "avatar_url": "https://avatars.githubusercontent.com/u/49783909?v=4", + "profile": "http://andreoss.sdf.org/", + "contributions": [ + "bug" + ] + }, + { + "login": "AndrewRayCode", + "name": "Andy Ray", + "avatar_url": "https://avatars.githubusercontent.com/u/79215?v=4", + "profile": "http://andrewray.me/", + "contributions": [ + "bug" + ] + }, + { + "login": "andrey-fomin", + "name": "Andrey Fomin", + "avatar_url": "https://avatars.githubusercontent.com/u/1307013?v=4", + "profile": "https://github.com/andrey-fomin", + "contributions": [ + "bug" + ] + }, + { + "login": "Andro72", + "name": "Andro72", + "avatar_url": "https://avatars.githubusercontent.com/u/1181872?v=4", + "profile": "https://github.com/Andro72", + "contributions": [ + "bug" + ] + }, + { + "login": "Androbin", + "name": "Robin Richtsfeld", + "avatar_url": "https://avatars.githubusercontent.com/u/16437156?v=4", + "profile": "https://androbin.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "Andrwyw", + "name": "Andrwyw", + "avatar_url": "https://avatars.githubusercontent.com/u/4827118?v=4", + "profile": "https://github.com/Andrwyw", + "contributions": [ + "bug" + ] + }, + { + "login": "Andy-2639", + "name": "Andy-2639", + "avatar_url": "https://avatars.githubusercontent.com/u/13392804?v=4", + "profile": "https://2639.dedyn.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "andypattenden", + "name": "Andy Pattenden", + "avatar_url": "https://avatars.githubusercontent.com/u/6861048?v=4", + "profile": "https://www.andypattenden.co.uk/", + "contributions": [ + "bug" + ] + }, + { + "login": "andyrobinson", + "name": "Andy Robinson", + "avatar_url": "https://avatars.githubusercontent.com/u/811521?v=4", + "profile": "https://github.com/andyrobinson", + "contributions": [ + "bug" + ] + }, + { + "login": "anicoara", + "name": "anicoara", + "avatar_url": "https://avatars.githubusercontent.com/u/835182?v=4", + "profile": "https://github.com/anicoara", + "contributions": [ + "bug" + ] + }, + { + "login": "anisimov-reveal", + "name": "Vasily Anisimov", + "avatar_url": "https://avatars.githubusercontent.com/u/69147166?v=4", + "profile": "https://github.com/anisimov-reveal", + "contributions": [ + "bug" + ] + }, + { + "login": "ankushsomani09", + "name": "Ankush Somani", + "avatar_url": "https://avatars.githubusercontent.com/u/2452618?v=4", + "profile": "https://github.com/ankushsomani09", + "contributions": [ + "bug" + ] + }, + { + "login": "anmolgkv", + "name": "Anmol Kumar", + "avatar_url": "https://avatars.githubusercontent.com/u/8535288?v=4", + "profile": "https://github.com/anmolgkv", + "contributions": [ + "bug" + ] + }, + { + "login": "AnthonyKot", + "name": "AnthonyKot", + "avatar_url": "https://avatars.githubusercontent.com/u/17334248?v=4", + "profile": "https://github.com/AnthonyKot", + "contributions": [ + "bug" + ] + }, + { + "login": "AravindHegde", + "name": "Aravind Hegde", + "avatar_url": "https://avatars.githubusercontent.com/u/50856835?v=4", + "profile": "https://github.com/AravindHegde", + "contributions": [ + "bug" + ] + }, + { + "login": "ardaasln", + "name": "Arda Aslan", + "avatar_url": "https://avatars.githubusercontent.com/u/22152020?v=4", + "profile": "https://github.com/ardaasln", + "contributions": [ + "bug" + ] + }, + { + "login": "ARentz07", + "name": "Alex Rentz", + "avatar_url": "https://avatars.githubusercontent.com/u/2237314?v=4", + "profile": "https://github.com/ARentz07", + "contributions": [ + "bug" + ] + }, + { + "login": "arifogel", + "name": "Ari Fogel", + "avatar_url": "https://avatars.githubusercontent.com/u/7737874?v=4", + "profile": "https://www.intentionet.com/team/ari-fogel/", + "contributions": [ + "bug" + ] + }, + { + "login": "arpitx165", + "name": "Arpit Koolwal", + "avatar_url": "https://avatars.githubusercontent.com/u/12485895?v=4", + "profile": "https://arpitx165.github.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "arturbosch", + "name": "Artur Bosch", + "avatar_url": "https://avatars.githubusercontent.com/u/20924106?v=4", + "profile": "https://github.com/arturbosch", + "contributions": [ + "bug" + ] + }, + { + "login": "arturdryomov", + "name": "Artur Dryomov", + "avatar_url": "https://avatars.githubusercontent.com/u/200401?v=4", + "profile": "https://github.com/arturdryomov", + "contributions": [ + "bug" + ] + }, + { + "login": "aruncollections", + "name": "arunprasathav", + "avatar_url": "https://avatars.githubusercontent.com/u/5299114?v=4", + "profile": "https://www.twitter.com/arunprasathav", + "contributions": [ + "bug" + ] + }, + { + "login": "asarkar", + "name": "Abhijit Sarkar", + "avatar_url": "https://avatars.githubusercontent.com/u/1302775?v=4", + "profile": "https://www.linkedin.com/in/abhijit-sarkar", + "contributions": [ + "bug" + ] + }, + { + "login": "ashishrana160796", + "name": "Ashish Rana", + "avatar_url": "https://avatars.githubusercontent.com/u/19948632?v=4", + "profile": "https://www.linkedin.com/in/ashishrana160796/", + "contributions": [ + "bug" + ] + }, + { + "login": "AshTheMash", + "name": "AshTheMash", + "avatar_url": "https://avatars.githubusercontent.com/u/67153866?v=4", + "profile": "https://github.com/AshTheMash", + "contributions": [ + "bug" + ] + }, + { + "login": "asiercamara", + "name": "asiercamara", + "avatar_url": "https://avatars.githubusercontent.com/u/38685011?v=4", + "profile": "https://github.com/asiercamara", + "contributions": [ + "bug" + ] + }, + { + "login": "aterai", + "name": "TERAI Atsuhiro", + "avatar_url": "https://avatars.githubusercontent.com/u/2842060?v=4", + "profile": "https://ateraimemo.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "atulkaushal", + "name": "Atul Kaushal", + "avatar_url": "https://avatars.githubusercontent.com/u/5075912?v=4", + "profile": "https://github.com/atulkaushal", + "contributions": [ + "bug" + ] + }, + { + "login": "augustboland", + "name": "August Boland", + "avatar_url": "https://avatars.githubusercontent.com/u/40546359?v=4", + "profile": "https://github.com/augustboland", + "contributions": [ + "bug" + ] + }, + { + "login": "AustinShalit", + "name": "Austin Shalit", + "avatar_url": "https://avatars.githubusercontent.com/u/3799260?v=4", + "profile": "https://github.com/AustinShalit", + "contributions": [ + "bug" + ] + }, + { + "login": "AustinTice", + "name": "Austin Tice", + "avatar_url": "https://avatars.githubusercontent.com/u/16217050?v=4", + "profile": "https://github.com/AustinTice", + "contributions": [ + "bug" + ] + }, + { + "login": "avesolovksyy", + "name": "avesolovksyy", + "avatar_url": "https://avatars.githubusercontent.com/u/46165403?v=4", + "profile": "https://github.com/avesolovksyy", + "contributions": [ + "bug" + ] + }, + { + "login": "avivmu", + "name": "avivmu", + "avatar_url": "https://avatars.githubusercontent.com/u/19804341?v=4", + "profile": "https://github.com/avivmu", + "contributions": [ + "bug" + ] + }, + { + "login": "awhitford", + "name": "Anthony Whitford", + "avatar_url": "https://avatars.githubusercontent.com/u/123887?v=4", + "profile": "https://anthonywhitford.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "axelbarfod1", + "name": "axelbarfod1", + "avatar_url": "https://avatars.githubusercontent.com/u/32651536?v=4", + "profile": "https://github.com/axelbarfod1", + "contributions": [ + "bug" + ] + }, + { + "login": "b-3-n", + "name": "b-3-n", + "avatar_url": "https://avatars.githubusercontent.com/u/7460509?v=4", + "profile": "https://github.com/b-3-n", + "contributions": [ + "bug" + ] + }, + { + "login": "balbhadra9", + "name": "balbhadra9", + "avatar_url": "https://avatars.githubusercontent.com/u/43748088?v=4", + "profile": "https://github.com/balbhadra9", + "contributions": [ + "bug" + ] + }, + { + "login": "barriosnahuel", + "name": "Nahuel Barrios", + "avatar_url": "https://avatars.githubusercontent.com/u/196234?v=4", + "profile": "http://bit.ly/nahuelbarrios", + "contributions": [ + "bug" + ] + }, + { + "login": "Bartheleway", + "name": "Barthélemy L.", + "avatar_url": "https://avatars.githubusercontent.com/u/1260779?v=4", + "profile": "https://github.com/Bartheleway", + "contributions": [ + "bug" + ] + }, + { + "login": "base23de", + "name": "base23de", + "avatar_url": "https://avatars.githubusercontent.com/u/37408753?v=4", + "profile": "https://github.com/base23de", + "contributions": [ + "bug" + ] + }, + { + "login": "bashagithub", + "name": "Chotu", + "avatar_url": "https://avatars.githubusercontent.com/u/25314601?v=4", + "profile": "https://github.com/bashagithub", + "contributions": [ + "bug" + ] + }, + { + "login": "baziorek", + "name": "G. Bazior", + "avatar_url": "https://avatars.githubusercontent.com/u/15332594?v=4", + "profile": "https://github.com/baziorek", + "contributions": [ + "bug" + ] + }, + { + "login": "bedla", + "name": "Ivo Šmíd", + "avatar_url": "https://avatars.githubusercontent.com/u/457542?v=4", + "profile": "https://www.linkedin.com/in/publicstaticvoidmain", + "contributions": [ + "bug" + ] + }, + { + "login": "Belle-PL", + "name": "Belle", + "avatar_url": "https://avatars.githubusercontent.com/u/86593041?v=4", + "profile": "https://github.com/Belle-PL", + "contributions": [ + "bug" + ] + }, + { + "login": "ben-manes", + "name": "Ben Manes", + "avatar_url": "https://avatars.githubusercontent.com/u/378614?v=4", + "profile": "https://www.linkedin.com/in/benmanes", + "contributions": [ + "bug" + ] + }, + { + "login": "benmccann", + "name": "Ben McCann", + "avatar_url": "https://avatars.githubusercontent.com/u/322311?v=4", + "profile": "http://www.benmccann.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "bennetyeesc", + "name": "Bennet S Yee", + "avatar_url": "https://avatars.githubusercontent.com/u/20605573?v=4", + "profile": "https://github.com/bennetyeesc", + "contributions": [ + "bug" + ] + }, + { + "login": "bergander", + "name": "bergander", + "avatar_url": "https://avatars.githubusercontent.com/u/8858497?v=4", + "profile": "https://github.com/bergander", + "contributions": [ + "bug" + ] + }, + { + "login": "BerndFarkaDyna", + "name": "Bernd Farka", + "avatar_url": "https://avatars.githubusercontent.com/u/39689620?v=4", + "profile": "https://github.com/BerndFarkaDyna", + "contributions": [ + "bug" + ] + }, + { + "login": "bhamail", + "name": "Dan Rollo", + "avatar_url": "https://avatars.githubusercontent.com/u/578919?v=4", + "profile": "https://github.com/bhamail", + "contributions": [ + "bug" + ] + }, + { + "login": "binu-r", + "name": "Binu R J", + "avatar_url": "https://avatars.githubusercontent.com/u/16700196?v=4", + "profile": "https://github.com/binu-r", + "contributions": [ + "bug" + ] + }, + { + "login": "birdflyer-lszo", + "name": "Bruno Ritz", + "avatar_url": "https://avatars.githubusercontent.com/u/511866?v=4", + "profile": "https://github.com/birdflyer-lszo", + "contributions": [ + "bug" + ] + }, + { + "login": "bkdotcom", + "name": "Brad Kent", + "avatar_url": "https://avatars.githubusercontent.com/u/2137404?v=4", + "profile": "http://www.bradkent.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "blacelle", + "name": "Benoit Lacelle", + "avatar_url": "https://avatars.githubusercontent.com/u/2117911?v=4", + "profile": "https://cormoran.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "blackmamo", + "name": "Matthew Amos", + "avatar_url": "https://avatars.githubusercontent.com/u/35695811?v=4", + "profile": "https://github.com/blackmamo", + "contributions": [ + "bug" + ] + }, + { + "login": "blerner", + "name": "Ben Lerner", + "avatar_url": "https://avatars.githubusercontent.com/u/918464?v=4", + "profile": "https://github.com/blerner", + "contributions": [ + "bug" + ] + }, + { + "login": "Blightbuster", + "name": "Blightbuster", + "avatar_url": "https://avatars.githubusercontent.com/u/24388294?v=4", + "profile": "https://github.com/Blightbuster", + "contributions": [ + "bug" + ] + }, + { + "login": "blindpirate", + "name": "Bo Zhang", + "avatar_url": "https://avatars.githubusercontent.com/u/12689835?v=4", + "profile": "https://github.com/blindpirate", + "contributions": [ + "bug" + ] + }, + { + "login": "bmacedo", + "name": "Bernardo Macêdo", + "avatar_url": "https://avatars.githubusercontent.com/u/3941421?v=4", + "profile": "https://github.com/bmacedo", + "contributions": [ + "bug" + ] + }, + { + "login": "bmbferreira", + "name": "Bruno Ferreira", + "avatar_url": "https://avatars.githubusercontent.com/u/626180?v=4", + "profile": "https://github.com/bmbferreira", + "contributions": [ + "bug" + ] + }, + { + "login": "bohni", + "name": "Stefan Bohn", + "avatar_url": "https://avatars.githubusercontent.com/u/1252254?v=4", + "profile": "https://github.com/bohni", + "contributions": [ + "bug" + ] + }, + { + "login": "boris-petrov", + "name": "Boris Petrov", + "avatar_url": "https://avatars.githubusercontent.com/u/278940?v=4", + "profile": "https://github.com/boris-petrov", + "contributions": [ + "bug" + ] + }, + { + "login": "brandonmikeska", + "name": "Brandon Mikeska", + "avatar_url": "https://avatars.githubusercontent.com/u/3750009?v=4", + "profile": "https://github.com/brandonmikeska", + "contributions": [ + "bug" + ] + }, + { + "login": "breizh31", + "name": "breizh31", + "avatar_url": "https://avatars.githubusercontent.com/u/15649505?v=4", + "profile": "https://github.com/breizh31", + "contributions": [ + "bug" + ] + }, + { + "login": "bthanki", + "name": "Bhargav Thanki", + "avatar_url": "https://avatars.githubusercontent.com/u/24976656?v=4", + "profile": "https://github.com/bthanki", + "contributions": [ + "bug" + ] + }, + { + "login": "caesarkim", + "name": "caesarkim", + "avatar_url": "https://avatars.githubusercontent.com/u/6069184?v=4", + "profile": "https://github.com/caesarkim", + "contributions": [ + "bug" + ] + }, + { + "login": "candrews", + "name": "Craig Andrews", + "avatar_url": "https://avatars.githubusercontent.com/u/194713?v=4", + "profile": "https://candrews.integralblue.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "carhartl", + "name": "Klaus Hartl", + "avatar_url": "https://avatars.githubusercontent.com/u/21918?v=4", + "profile": "https://github.com/carhartl", + "contributions": [ + "bug" + ] + }, + { + "login": "carolyujing", + "name": "carolyujing", + "avatar_url": "https://avatars.githubusercontent.com/u/6173449?v=4", + "profile": "https://github.com/carolyujing", + "contributions": [ + "bug" + ] + }, + { + "login": "Casara", + "name": "Rodrigo Casara", + "avatar_url": "https://avatars.githubusercontent.com/u/2224686?v=4", + "profile": "https://github.com/Casara", + "contributions": [ + "bug" + ] + }, + { + "login": "CasualSuperman", + "name": "Bobby Wertman", + "avatar_url": "https://avatars.githubusercontent.com/u/516927?v=4", + "profile": "https://github.com/CasualSuperman", + "contributions": [ + "bug" + ] + }, + { + "login": "catalandres", + "name": "Andrés Catalán", + "avatar_url": "https://avatars.githubusercontent.com/u/1649037?v=4", + "profile": "https://github.com/catalandres", + "contributions": [ + "bug" + ] + }, + { + "login": "cdancy", + "name": "Christopher Dancy", + "avatar_url": "https://avatars.githubusercontent.com/u/7871459?v=4", + "profile": "https://github.com/cdancy", + "contributions": [ + "bug" + ] + }, + { + "login": "cesares-basilico", + "name": "cesares-basilico", + "avatar_url": "https://avatars.githubusercontent.com/u/14895641?v=4", + "profile": "https://github.com/cesares-basilico", + "contributions": [ + "bug" + ] + }, + { + "login": "champo", + "name": "Juan Pablo Civile", + "avatar_url": "https://avatars.githubusercontent.com/u/202728?v=4", + "profile": "https://twitter.com/elchampo", + "contributions": [ + "bug" + ] + }, + { + "login": "chimpytt", + "name": "Drew Hall", + "avatar_url": "https://avatars.githubusercontent.com/u/3532693?v=4", + "profile": "https://github.com/chimpytt", + "contributions": [ + "bug" + ] + }, + { + "login": "chonton", + "name": "Chas Honton", + "avatar_url": "https://avatars.githubusercontent.com/u/1444125?v=4", + "profile": "http://www.linkedin.com/in/chonton", + "contributions": [ + "bug" + ] + }, + { + "login": "christianhujer", + "name": "Christian Hujer", + "avatar_url": "https://avatars.githubusercontent.com/u/692345?v=4", + "profile": "https://nelkinda.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "ChristianWulf", + "name": "ChristianWulf", + "avatar_url": "https://avatars.githubusercontent.com/u/3480324?v=4", + "profile": "https://www.i-love-software-engineering.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "chrite", + "name": "chrite", + "avatar_url": "https://avatars.githubusercontent.com/u/53291173?v=4", + "profile": "https://github.com/chrite", + "contributions": [ + "bug" + ] + }, + { + "login": "ChuckJonas", + "name": "Charlie Jonas", + "avatar_url": "https://avatars.githubusercontent.com/u/5217568?v=4", + "profile": "https://github.com/ChuckJonas", + "contributions": [ + "bug" + ] + }, + { + "login": "clsaa", + "name": "任贵杰", + "avatar_url": "https://avatars.githubusercontent.com/u/32028545?v=4", + "profile": "https://github.com/clsaa", + "contributions": [ + "bug" + ] + }, + { + "login": "cmuchinsky", + "name": "Craig Muchinsky", + "avatar_url": "https://avatars.githubusercontent.com/u/23175991?v=4", + "profile": "https://github.com/cmuchinsky", + "contributions": [ + "bug" + ] + }, + { + "login": "cobratbq", + "name": "cobratbq", + "avatar_url": "https://avatars.githubusercontent.com/u/1936470?v=4", + "profile": "https://www.dannyvanheumen.nl/", + "contributions": [ + "bug" + ] + }, + { + "login": "codacy-badger", + "name": "Codacy Badger", + "avatar_url": "https://avatars.githubusercontent.com/u/23704769?v=4", + "profile": "https://www.codacy.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "codecop", + "name": "Peter Kofler", + "avatar_url": "https://avatars.githubusercontent.com/u/830028?v=4", + "profile": "https://www.code-cop.org/", + "contributions": [ + "bug" + ] + }, + { + "login": "Code-Nil", + "name": "Code-Nil", + "avatar_url": "https://avatars.githubusercontent.com/u/30105281?v=4", + "profile": "https://github.com/Code-Nil", + "contributions": [ + "bug" + ] + }, + { + "login": "coladict", + "name": "coladict", + "avatar_url": "https://avatars.githubusercontent.com/u/1909837?v=4", + "profile": "https://github.com/coladict", + "contributions": [ + "bug" + ] + }, + { + "login": "ColColonCleaner", + "name": "ColColonCleaner", + "avatar_url": "https://avatars.githubusercontent.com/u/1903507?v=4", + "profile": "https://github.com/ColColonCleaner", + "contributions": [ + "bug" + ] + }, + { + "login": "colini", + "name": "Colin Ingarfield", + "avatar_url": "https://avatars.githubusercontent.com/u/2913561?v=4", + "profile": "https://github.com/colini", + "contributions": [ + "bug" + ] + }, + { + "login": "coola", + "name": "Michał Kuliński", + "avatar_url": "https://avatars.githubusercontent.com/u/83182?v=4", + "profile": "https://github.com/coola", + "contributions": [ + "bug" + ] + }, + { + "login": "cosmoJFH", + "name": "cosmoJFH", + "avatar_url": "https://avatars.githubusercontent.com/u/19255991?v=4", + "profile": "https://github.com/cosmoJFH", + "contributions": [ + "bug" + ] + }, + { + "login": "C-Otto", + "name": "Carsten Otto", + "avatar_url": "https://avatars.githubusercontent.com/u/344948?v=4", + "profile": "https://c-otto.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "CrazyUnderdog", + "name": "Jan Brümmer", + "avatar_url": "https://avatars.githubusercontent.com/u/23554953?v=4", + "profile": "https://github.com/CrazyUnderdog", + "contributions": [ + "bug" + ] + }, + { + "login": "cristalp", + "name": "cristalp", + "avatar_url": "https://avatars.githubusercontent.com/u/12525759?v=4", + "profile": "https://github.com/cristalp", + "contributions": [ + "bug" + ] + }, + { + "login": "cropredyHelix", + "name": "Eric Kintzer", + "avatar_url": "https://avatars.githubusercontent.com/u/25649155?v=4", + "profile": "https://github.com/cropredyHelix", + "contributions": [ + "bug" + ] + }, + { + "login": "crunsk", + "name": "crunsk", + "avatar_url": "https://avatars.githubusercontent.com/u/5631441?v=4", + "profile": "https://github.com/crunsk", + "contributions": [ + "bug" + ] + }, + { + "login": "cshu", + "name": "Eric", + "avatar_url": "https://avatars.githubusercontent.com/u/3754290?v=4", + "profile": "http://stackoverflow.com/users/2419922/cshu", + "contributions": [ + "bug" + ] + }, + { + "login": "cwholmes", + "name": "cwholmes", + "avatar_url": "https://avatars.githubusercontent.com/u/14796526?v=4", + "profile": "https://github.com/cwholmes", + "contributions": [ + "bug" + ] + }, + { + "login": "cyberjj999", + "name": "cyberjj999", + "avatar_url": "https://avatars.githubusercontent.com/u/51283594?v=4", + "profile": "https://github.com/cyberjj999", + "contributions": [ + "bug" + ] + }, + { + "login": "cynthux", + "name": "Betina Cynthia Mamani", + "avatar_url": "https://avatars.githubusercontent.com/u/7284580?v=4", + "profile": "https://github.com/cynthux", + "contributions": [ + "bug" + ] + }, + { + "login": "cyw3", + "name": "cyw3", + "avatar_url": "https://avatars.githubusercontent.com/u/11549103?v=4", + "profile": "https://github.com/cyw3", + "contributions": [ + "bug" + ] + }, + { + "login": "d1ss0nanz", + "name": "d1ss0nanz", + "avatar_url": "https://avatars.githubusercontent.com/u/7532776?v=4", + "profile": "https://github.com/d1ss0nanz", + "contributions": [ + "bug" + ] + }, + { + "login": "DaDummy", + "name": "Christoffer Anselm", + "avatar_url": "https://avatars.githubusercontent.com/u/11466036?v=4", + "profile": "https://github.com/DaDummy", + "contributions": [ + "bug" + ] + }, + { + "login": "dailyco", + "name": "YuJin Kim", + "avatar_url": "https://avatars.githubusercontent.com/u/48382813?v=4", + "profile": "https://dailyco.github.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "dakov", + "name": "David Kovařík", + "avatar_url": "https://avatars.githubusercontent.com/u/4954378?v=4", + "profile": "https://github.com/dakov", + "contributions": [ + "bug" + ] + }, + { + "login": "danberindei", + "name": "Dan Berindei", + "avatar_url": "https://avatars.githubusercontent.com/u/504907?v=4", + "profile": "https://github.com/danberindei", + "contributions": [ + "bug" + ] + }, + { + "login": "danbrycefairsailcom", + "name": "danbrycefairsailcom", + "avatar_url": "https://avatars.githubusercontent.com/u/25037396?v=4", + "profile": "https://github.com/danbrycefairsailcom", + "contributions": [ + "bug" + ] + }, + { + "login": "danjee", + "name": "Daniel Jipa", + "avatar_url": "https://avatars.githubusercontent.com/u/3084216?v=4", + "profile": "https://github.com/danjee", + "contributions": [ + "bug" + ] + }, + { + "login": "DanySK", + "name": "Danilo Pianini", + "avatar_url": "https://avatars.githubusercontent.com/u/1991673?v=4", + "profile": "http://www.danilopianini.org/", + "contributions": [ + "bug" + ] + }, + { + "login": "dariansanity", + "name": "dariansanity", + "avatar_url": "https://avatars.githubusercontent.com/u/28553192?v=4", + "profile": "https://github.com/dariansanity", + "contributions": [ + "bug" + ] + }, + { + "login": "darrenmiliband", + "name": "darrenmiliband", + "avatar_url": "https://avatars.githubusercontent.com/u/62128185?v=4", + "profile": "https://github.com/darrenmiliband", + "contributions": [ + "bug" + ] + }, + { + "login": "davidburstrom", + "name": "davidburstrom", + "avatar_url": "https://avatars.githubusercontent.com/u/1671931?v=4", + "profile": "https://github.com/davidburstrom", + "contributions": [ + "bug" + ] + }, + { + "login": "davidgoate", + "name": "David Goaté", + "avatar_url": "https://avatars.githubusercontent.com/u/8789912?v=4", + "profile": "https://github.com/davidgoate", + "contributions": [ + "bug" + ] + }, + { + "login": "davidmichaelkarr", + "name": "David M. Karr (fullname at gmail.com)", + "avatar_url": "https://avatars.githubusercontent.com/u/5566419?v=4", + "profile": "https://github.com/davidmichaelkarr", + "contributions": [ + "bug" + ] + }, + { + "login": "David-Renz", + "name": "David Renz", + "avatar_url": "https://avatars.githubusercontent.com/u/8180460?v=4", + "profile": "https://github.com/David-Renz", + "contributions": [ + "bug" + ] + }, + { + "login": "dbirkman-paloalto", + "name": "dbirkman-paloalto", + "avatar_url": "https://avatars.githubusercontent.com/u/53145780?v=4", + "profile": "https://github.com/dbirkman-paloalto", + "contributions": [ + "bug" + ] + }, + { + "login": "ddashenkov", + "name": "Dmytro Dashenkov", + "avatar_url": "https://avatars.githubusercontent.com/u/17772311?v=4", + "profile": "https://github.com/ddashenkov", + "contributions": [ + "bug" + ] + }, + { + "login": "deepak-patra", + "name": "deepak-patra", + "avatar_url": "https://avatars.githubusercontent.com/u/8747728?v=4", + "profile": "https://github.com/deepak-patra", + "contributions": [ + "bug" + ] + }, + { + "login": "deki", + "name": "Dennis Kieselhorst", + "avatar_url": "https://avatars.githubusercontent.com/u/858827?v=4", + "profile": "http://www.dekies.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "dellgreen", + "name": "Dell Green", + "avatar_url": "https://avatars.githubusercontent.com/u/12861109?v=4", + "profile": "http://www.ideaworks.co.uk/", + "contributions": [ + "bug" + ] + }, + { + "login": "demonfiddler", + "name": "Adrian Price", + "avatar_url": "https://avatars.githubusercontent.com/u/14012347?v=4", + "profile": "https://github.com/demonfiddler", + "contributions": [ + "bug" + ] + }, + { + "login": "denisZhe", + "name": "Den", + "avatar_url": "https://avatars.githubusercontent.com/u/26335964?v=4", + "profile": "https://github.com/denisZhe", + "contributions": [ + "bug" + ] + }, + { + "login": "derekm", + "name": "Derek P. Moore", + "avatar_url": "https://avatars.githubusercontent.com/u/379469?v=4", + "profile": "https://github.com/derekm", + "contributions": [ + "bug" + ] + }, + { + "login": "dgroup", + "name": "Yurii Dubinka", + "avatar_url": "https://avatars.githubusercontent.com/u/1651114?v=4", + "profile": "https://www.linkedin.com/in/lazylead", + "contributions": [ + "bug" + ] + }, + { + "login": "Dichotomia", + "name": "Dichotomia", + "avatar_url": "https://avatars.githubusercontent.com/u/5593198?v=4", + "profile": "https://github.com/Dichotomia", + "contributions": [ + "bug" + ] + }, + { + "login": "dimas-b", + "name": "Dmitri Bourlatchkov", + "avatar_url": "https://avatars.githubusercontent.com/u/40603221?v=4", + "profile": "https://github.com/dimas-b", + "contributions": [ + "bug" + ] + }, + { + "login": "dinesh150", + "name": "dinesh150", + "avatar_url": "https://avatars.githubusercontent.com/u/88079095?v=4", + "profile": "https://github.com/dinesh150", + "contributions": [ + "bug" + ] + }, + { + "login": "diziaq", + "name": "diziaq", + "avatar_url": "https://avatars.githubusercontent.com/u/6733997?v=4", + "profile": "https://github.com/diziaq", + "contributions": [ + "bug" + ] + }, + { + "login": "djh82", + "name": "David", + "avatar_url": "https://avatars.githubusercontent.com/u/1810493?v=4", + "profile": "https://github.com/djh82", + "contributions": [ + "bug" + ] + }, + { + "login": "dkadams", + "name": "Dylan Adams", + "avatar_url": "https://avatars.githubusercontent.com/u/1144945?v=4", + "profile": "https://github.com/dkadams", + "contributions": [ + "bug" + ] + }, + { + "login": "dmitrykuzmin", + "name": "Dmitriy Kuzmin", + "avatar_url": "https://avatars.githubusercontent.com/u/5636313?v=4", + "profile": "https://github.com/dmitrykuzmin", + "contributions": [ + "bug" + ] + }, + { + "login": "don-vip", + "name": "Vincent Privat", + "avatar_url": "https://avatars.githubusercontent.com/u/596867?v=4", + "profile": "https://github.com/don-vip", + "contributions": [ + "bug" + ] + }, + { + "login": "dotdoom", + "name": "Artem Sheremet", + "avatar_url": "https://avatars.githubusercontent.com/u/561122?v=4", + "profile": "https://github.com/dotdoom", + "contributions": [ + "bug" + ] + }, + { + "login": "dpalic", + "name": "Darko", + "avatar_url": "https://avatars.githubusercontent.com/u/1399203?v=4", + "profile": "https://github.com/dpalic", + "contributions": [ + "bug" + ] + }, + { + "login": "dpilafian", + "name": "Dem Pilafian", + "avatar_url": "https://avatars.githubusercontent.com/u/119555?v=4", + "profile": "https://centerkey.com/dem", + "contributions": [ + "bug" + ] + }, + { + "login": "dreaminzero-cell", + "name": "dreaminpast123", + "avatar_url": "https://avatars.githubusercontent.com/u/24776498?v=4", + "profile": "https://github.com/dreaminzero-cell", + "contributions": [ + "bug" + ] + }, + { + "login": "DReigada", + "name": "Daniel Reigada", + "avatar_url": "https://avatars.githubusercontent.com/u/11428771?v=4", + "profile": "https://github.com/DReigada", + "contributions": [ + "bug" + ] + }, + { + "login": "duanyanan", + "name": "duanyanan", + "avatar_url": "https://avatars.githubusercontent.com/u/22003836?v=4", + "profile": "https://github.com/duanyanan", + "contributions": [ + "bug" + ] + }, + { + "login": "dutt-sanjay", + "name": "dutt-sanjay", + "avatar_url": "https://avatars.githubusercontent.com/u/70677817?v=4", + "profile": "https://github.com/dutt-sanjay", + "contributions": [ + "bug" + ] + }, + { + "login": "dvdtknsn", + "name": "David Atkinson", + "avatar_url": "https://avatars.githubusercontent.com/u/4223632?v=4", + "profile": "https://github.com/dvdtknsn", + "contributions": [ + "bug" + ] + }, + { + "login": "dylanleung", + "name": "dylanleung", + "avatar_url": "https://avatars.githubusercontent.com/u/1852360?v=4", + "profile": "https://github.com/dylanleung", + "contributions": [ + "bug" + ] + }, + { + "login": "dzeigler", + "name": "dzeigler", + "avatar_url": "https://avatars.githubusercontent.com/u/1615832?v=4", + "profile": "https://github.com/dzeigler", + "contributions": [ + "bug" + ] + }, + { + "login": "eekboom", + "name": "Stephen Friedrich", + "avatar_url": "https://avatars.githubusercontent.com/u/717179?v=4", + "profile": "https://github.com/eekboom", + "contributions": [ + "bug" + ] + }, + { + "login": "Egor18", + "name": "Egor Bredikhin", + "avatar_url": "https://avatars.githubusercontent.com/u/32983915?v=4", + "profile": "https://github.com/Egor18", + "contributions": [ + "bug" + ] + }, + { + "login": "eikemeier", + "name": "Oliver Eikemeier", + "avatar_url": "https://avatars.githubusercontent.com/u/604196?v=4", + "profile": "http://www.eikemeier.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "ekkirala", + "name": "ekkirala", + "avatar_url": "https://avatars.githubusercontent.com/u/44954455?v=4", + "profile": "https://github.com/ekkirala", + "contributions": [ + "bug" + ] + }, + { + "login": "eldereng", + "name": "Elder S.", + "avatar_url": "https://avatars.githubusercontent.com/u/8397171?v=4", + "profile": "https://github.com/eldereng", + "contributions": [ + "bug" + ] + }, + { + "login": "emersonmoura", + "name": "emersonmoura", + "avatar_url": "https://avatars.githubusercontent.com/u/5419868?v=4", + "profile": "https://github.com/emersonmoura", + "contributions": [ + "bug" + ] + }, + { + "login": "EmileSonneveld", + "name": "Emile", + "avatar_url": "https://avatars.githubusercontent.com/u/2298426?v=4", + "profile": "http://emilesonneveld.be/", + "contributions": [ + "bug" + ] + }, + { + "login": "eperret", + "name": "Eric Perret", + "avatar_url": "https://avatars.githubusercontent.com/u/5377458?v=4", + "profile": "https://www.ericperrets.info/", + "contributions": [ + "bug" + ] + }, + { + "login": "epkugelmass", + "name": "Elan P. Kugelmass", + "avatar_url": "https://avatars.githubusercontent.com/u/6265378?v=4", + "profile": "https://github.com/epkugelmass", + "contributions": [ + "bug" + ] + }, + { + "login": "erichlf", + "name": "Erich L Foster", + "avatar_url": "https://avatars.githubusercontent.com/u/1392578?v=4", + "profile": "https://github.com/erichlf", + "contributions": [ + "bug" + ] + }, + { + "login": "erik4github", + "name": "Erik Bleske", + "avatar_url": "https://avatars.githubusercontent.com/u/28544995?v=4", + "profile": "https://github.com/erik4github", + "contributions": [ + "bug" + ] + }, + { + "login": "esquires", + "name": "Eric Squires", + "avatar_url": "https://avatars.githubusercontent.com/u/9272110?v=4", + "profile": "https://github.com/esquires", + "contributions": [ + "bug" + ] + }, + { + "login": "fanlw0816", + "name": "frankl", + "avatar_url": "https://avatars.githubusercontent.com/u/22781995?v=4", + "profile": "https://github.com/fanlw0816", + "contributions": [ + "bug" + ] + }, + { + "login": "farmaazon", + "name": "Adam Obuchowicz", + "avatar_url": "https://avatars.githubusercontent.com/u/3919101?v=4", + "profile": "https://github.com/farmaazon", + "contributions": [ + "bug" + ] + }, + { + "login": "fblampe", + "name": "Felix Lampe", + "avatar_url": "https://avatars.githubusercontent.com/u/35843701?v=4", + "profile": "https://github.com/fblampe", + "contributions": [ + "bug" + ] + }, + { + "login": "fciancio", + "name": "Facundo", + "avatar_url": "https://avatars.githubusercontent.com/u/7189753?v=4", + "profile": "https://github.com/fciancio", + "contributions": [ + "bug" + ] + }, + { + "login": "fedegiust", + "name": "Federico Giust", + "avatar_url": "https://avatars.githubusercontent.com/u/1804862?v=4", + "profile": "https://www.linkedin.com/in/federicogiust/", + "contributions": [ + "bug" + ] + }, + { + "login": "Fernal73", + "name": "Linus Fernandes", + "avatar_url": "https://avatars.githubusercontent.com/u/24714685?v=4", + "profile": "https://linusfernandes.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "fflatorre", + "name": "Francesco la Torre", + "avatar_url": "https://avatars.githubusercontent.com/u/1547188?v=4", + "profile": "https://github.com/fflatorre", + "contributions": [ + "bug" + ] + }, + { + "login": "filipgolonka", + "name": "Filip Golonka", + "avatar_url": "https://avatars.githubusercontent.com/u/2419247?v=4", + "profile": "https://github.com/filipgolonka", + "contributions": [ + "bug" + ] + }, + { + "login": "fluxroot", + "name": "Phokham Nonava", + "avatar_url": "https://avatars.githubusercontent.com/u/247365?v=4", + "profile": "https://github.com/fluxroot", + "contributions": [ + "bug" + ] + }, + { + "login": "foxmason", + "name": "foxmason", + "avatar_url": "https://avatars.githubusercontent.com/u/33361071?v=4", + "profile": "https://github.com/foxmason", + "contributions": [ + "bug" + ] + }, + { + "login": "franciscodua", + "name": "Francisco Duarte", + "avatar_url": "https://avatars.githubusercontent.com/u/6960449?v=4", + "profile": "https://github.com/franciscodua", + "contributions": [ + "bug" + ] + }, + { + "login": "frankegabor", + "name": "frankegabor", + "avatar_url": "https://avatars.githubusercontent.com/u/13273444?v=4", + "profile": "https://github.com/frankegabor", + "contributions": [ + "bug" + ] + }, + { + "login": "freafrea", + "name": "freafrea", + "avatar_url": "https://avatars.githubusercontent.com/u/39403091?v=4", + "profile": "https://github.com/freafrea", + "contributions": [ + "bug" + ] + }, + { + "login": "friederbluemle", + "name": "Frieder Bluemle", + "avatar_url": "https://avatars.githubusercontent.com/u/743291?v=4", + "profile": "https://github.com/friederbluemle", + "contributions": [ + "bug" + ] + }, + { + "login": "frizbog", + "name": "Matt Harrah", + "avatar_url": "https://avatars.githubusercontent.com/u/2901857?v=4", + "profile": "https://github.com/frizbog", + "contributions": [ + "bug" + ] + }, + { + "login": "fsapatin", + "name": "fsapatin", + "avatar_url": "https://avatars.githubusercontent.com/u/10675254?v=4", + "profile": "https://github.com/fsapatin", + "contributions": [ + "bug" + ] + }, + { + "login": "fsherstobitov", + "name": "Fedor Sherstobitov", + "avatar_url": "https://avatars.githubusercontent.com/u/1047478?v=4", + "profile": "https://github.com/fsherstobitov", + "contributions": [ + "bug" + ] + }, + { + "login": "FWDekker", + "name": "F.W. Dekker", + "avatar_url": "https://avatars.githubusercontent.com/u/13442533?v=4", + "profile": "https://fwdekker.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "genoud", + "name": "Genoud Magloire", + "avatar_url": "https://avatars.githubusercontent.com/u/10148667?v=4", + "profile": "https://www.linkedin.com/in/genoud6/", + "contributions": [ + "bug" + ] + }, + { + "login": "Geoffrey555", + "name": "Geoffrey555", + "avatar_url": "https://avatars.githubusercontent.com/u/41955002?v=4", + "profile": "https://github.com/Geoffrey555", + "contributions": [ + "bug" + ] + }, + { + "login": "ghenkes", + "name": "Gabe Henkes", + "avatar_url": "https://avatars.githubusercontent.com/u/11396826?v=4", + "profile": "https://github.com/ghenkes", + "contributions": [ + "bug" + ] + }, + { + "login": "ghost", + "name": "Deleted user", + "avatar_url": "https://avatars.githubusercontent.com/u/10137?v=4", + "profile": "https://github.com/ghost", + "contributions": [ + "bug" + ] + }, + { + "login": "GiantCrocodile", + "name": "Clemens Prill", + "avatar_url": "https://avatars.githubusercontent.com/u/6271057?v=4", + "profile": "http://clemensprill.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "giorgimode", + "name": "Gio", + "avatar_url": "https://avatars.githubusercontent.com/u/13137407?v=4", + "profile": "https://github.com/giorgimode", + "contributions": [ + "bug" + ] + }, + { + "login": "gitter-badger", + "name": "The Gitter Badger", + "avatar_url": "https://avatars.githubusercontent.com/u/8518239?v=4", + "profile": "https://gitter.im/", + "contributions": [ + "bug" + ] + }, + { + "login": "golpira", + "name": "David Golpira", + "avatar_url": "https://avatars.githubusercontent.com/u/1809854?v=4", + "profile": "http://www.alternatecomputing.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "golszewski86", + "name": "Grzegorz Olszewski", + "avatar_url": "https://avatars.githubusercontent.com/u/45224442?v=4", + "profile": "https://github.com/golszewski86", + "contributions": [ + "bug" + ] + }, + { + "login": "GooDer", + "name": "GooDer", + "avatar_url": "https://avatars.githubusercontent.com/u/953850?v=4", + "profile": "https://github.com/GooDer", + "contributions": [ + "bug" + ] + }, + { + "login": "gpbp", + "name": "Pham Hai Trung", + "avatar_url": "https://avatars.githubusercontent.com/u/18648177?v=4", + "profile": "https://github.com/gpbp", + "contributions": [ + "bug" + ] + }, + { + "login": "gracia19", + "name": "gracia19", + "avatar_url": "https://avatars.githubusercontent.com/u/32557952?v=4", + "profile": "https://github.com/gracia19", + "contributions": [ + "bug" + ] + }, + { + "login": "grandinj", + "name": "Noel Grandin", + "avatar_url": "https://avatars.githubusercontent.com/u/796121?v=4", + "profile": "https://github.com/grandinj", + "contributions": [ + "bug" + ] + }, + { + "login": "gregorriegler", + "name": "Gregor Riegler", + "avatar_url": "https://avatars.githubusercontent.com/u/5053662?v=4", + "profile": "https://gregorriegler.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "grv87", + "name": "Basil Peace", + "avatar_url": "https://avatars.githubusercontent.com/u/2035596?v=4", + "profile": "https://github.com/grv87", + "contributions": [ + "bug" + ] + }, + { + "login": "guettli", + "name": "Thomas Güttler", + "avatar_url": "https://avatars.githubusercontent.com/u/414336?v=4", + "profile": "http://www.thomas-guettler.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "gurmsc5", + "name": "gurmsc5", + "avatar_url": "https://avatars.githubusercontent.com/u/26914263?v=4", + "profile": "https://github.com/gurmsc5", + "contributions": [ + "bug" + ] + }, + { + "login": "gustavopcassol", + "name": "Gustavo Krieger", + "avatar_url": "https://avatars.githubusercontent.com/u/17629700?v=4", + "profile": "https://github.com/gustavopcassol", + "contributions": [ + "bug" + ] + }, + { + "login": "guxiaonian", + "name": "fairy", + "avatar_url": "https://avatars.githubusercontent.com/u/24585054?v=4", + "profile": "https://juejin.cn/user/1063982985642525", + "contributions": [ + "bug" + ] + }, + { + "login": "GuyPaddock", + "name": "Guy Elsmore-Paddock", + "avatar_url": "https://avatars.githubusercontent.com/u/2631799?v=4", + "profile": "http://www.inveniem.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "haigsn", + "name": "haigsn", + "avatar_url": "https://avatars.githubusercontent.com/u/52993319?v=4", + "profile": "https://github.com/haigsn", + "contributions": [ + "bug" + ] + }, + { + "login": "harsh-kukreja", + "name": "Harsh Kukreja", + "avatar_url": "https://avatars.githubusercontent.com/u/40023562?v=4", + "profile": "https://github.com/harsh-kukreja", + "contributions": [ + "bug" + ] + }, + { + "login": "havrikov", + "name": "Nikolas Havrikov", + "avatar_url": "https://avatars.githubusercontent.com/u/29175267?v=4", + "profile": "https://github.com/havrikov", + "contributions": [ + "bug" + ] + }, + { + "login": "hboutemy", + "name": "Hervé Boutemy", + "avatar_url": "https://avatars.githubusercontent.com/u/237462?v=4", + "profile": "http://people.apache.org/~hboutemy", + "contributions": [ + "bug" + ] + }, + { + "login": "hemanshu070", + "name": "hemanshu070", + "avatar_url": "https://avatars.githubusercontent.com/u/32012651?v=4", + "profile": "https://github.com/hemanshu070", + "contributions": [ + "bug" + ] + }, + { + "login": "henrik242", + "name": "henrik242", + "avatar_url": "https://avatars.githubusercontent.com/u/129931?v=4", + "profile": "https://github.com/henrik242", + "contributions": [ + "bug" + ] + }, + { + "login": "hgodinez89", + "name": "Hanzel Godinez", + "avatar_url": "https://avatars.githubusercontent.com/u/14959862?v=4", + "profile": "https://hanzelgodinez.dev/", + "contributions": [ + "bug" + ] + }, + { + "login": "Hokwang", + "name": "Hokwang Lee", + "avatar_url": "https://avatars.githubusercontent.com/u/1247030?v=4", + "profile": "https://github.com/Hokwang", + "contributions": [ + "bug" + ] + }, + { + "login": "holthauj", + "name": "Josh Holthaus", + "avatar_url": "https://avatars.githubusercontent.com/u/2595766?v=4", + "profile": "https://github.com/holthauj", + "contributions": [ + "bug" + ] + }, + { + "login": "hongpuwu", + "name": "hongpuwu", + "avatar_url": "https://avatars.githubusercontent.com/u/19198552?v=4", + "profile": "https://github.com/hongpuwu", + "contributions": [ + "bug" + ] + }, + { + "login": "HoushCE29", + "name": "Charlie Housh", + "avatar_url": "https://avatars.githubusercontent.com/u/22387324?v=4", + "profile": "https://github.com/HoushCE29", + "contributions": [ + "bug" + ] + }, + { + "login": "hpandeycodeit", + "name": "Himanshu Pandey", + "avatar_url": "https://avatars.githubusercontent.com/u/21266105?v=4", + "profile": "https://github.com/hpandeycodeit", + "contributions": [ + "bug" + ] + }, + { + "login": "hudec117", + "name": "Aurel Hudec", + "avatar_url": "https://avatars.githubusercontent.com/u/10981949?v=4", + "profile": "https://github.com/hudec117", + "contributions": [ + "bug" + ] + }, + { + "login": "huxi", + "name": "Jörn Huxhorn", + "avatar_url": "https://avatars.githubusercontent.com/u/119647?v=4", + "profile": "http://lilith.huxhorn.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "iccengan", + "name": "Iccen Gan", + "avatar_url": "https://avatars.githubusercontent.com/u/10875468?v=4", + "profile": "https://github.com/iccengan", + "contributions": [ + "bug" + ] + }, + { + "login": "ief2009", + "name": "guo fei", + "avatar_url": "https://avatars.githubusercontent.com/u/1955449?v=4", + "profile": "https://github.com/ief2009", + "contributions": [ + "bug" + ] + }, + { + "login": "igniti-gmbh", + "name": "igniti GmbH", + "avatar_url": "https://avatars.githubusercontent.com/u/7207145?v=4", + "profile": "http://www.igniti.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "igormoreno", + "name": "Igor Moreno", + "avatar_url": "https://avatars.githubusercontent.com/u/229440?v=4", + "profile": "https://github.com/igormoreno", + "contributions": [ + "bug" + ] + }, + { + "login": "iguerini", + "name": "Ivano Guerini", + "avatar_url": "https://avatars.githubusercontent.com/u/5718273?v=4", + "profile": "https://github.com/iguerini", + "contributions": [ + "bug" + ] + }, + { + "login": "ilovezfs", + "name": "ilovezfs", + "avatar_url": "https://avatars.githubusercontent.com/u/5268928?v=4", + "profile": "https://github.com/ilovezfs", + "contributions": [ + "bug" + ] + }, + { + "login": "ImmortalPawn", + "name": "Mladjan Gadzic", + "avatar_url": "https://avatars.githubusercontent.com/u/30688679?v=4", + "profile": "https://github.com/ImmortalPawn", + "contributions": [ + "bug" + ] + }, + { + "login": "impmihai", + "name": "Mihai Ionut", + "avatar_url": "https://avatars.githubusercontent.com/u/22995337?v=4", + "profile": "https://github.com/impmihai", + "contributions": [ + "bug" + ] + }, + { + "login": "Intelesis-MS", + "name": "Intelesis-MS", + "avatar_url": "https://avatars.githubusercontent.com/u/16139113?v=4", + "profile": "https://github.com/Intelesis-MS", + "contributions": [ + "bug" + ] + }, + { + "login": "ishanSrt", + "name": "Ishan Srivastava", + "avatar_url": "https://avatars.githubusercontent.com/u/29859362?v=4", + "profile": "https://github.com/ishanSrt", + "contributions": [ + "bug" + ] + }, + { + "login": "itaigilo", + "name": "itaigilo", + "avatar_url": "https://avatars.githubusercontent.com/u/13402361?v=4", + "profile": "https://github.com/itaigilo", + "contributions": [ + "bug" + ] + }, + { + "login": "itirabasso", + "name": "Ignacio Mariano Tirabasso", + "avatar_url": "https://avatars.githubusercontent.com/u/627816?v=4", + "profile": "https://github.com/itirabasso", + "contributions": [ + "bug" + ] + }, + { + "login": "itsmebasti", + "name": "Sebastian Schwarz", + "avatar_url": "https://avatars.githubusercontent.com/u/12232063?v=4", + "profile": "https://github.com/itsmebasti", + "contributions": [ + "bug" + ] + }, + { + "login": "jainrish", + "name": "Rishabh Jain", + "avatar_url": "https://avatars.githubusercontent.com/u/25207823?v=4", + "profile": "http://rishjain.me/", + "contributions": [ + "bug" + ] + }, + { + "login": "jakehemmerle", + "name": "Jake Hemmerle", + "avatar_url": "https://avatars.githubusercontent.com/u/8061957?v=4", + "profile": "https://github.com/jakehemmerle", + "contributions": [ + "bug" + ] + }, + { + "login": "jakivey32", + "name": "jakivey32", + "avatar_url": "https://avatars.githubusercontent.com/u/36869603?v=4", + "profile": "https://github.com/jakivey32", + "contributions": [ + "bug" + ] + }, + { + "login": "jarquile", + "name": "Joshua S Arquilevich", + "avatar_url": "https://avatars.githubusercontent.com/u/16008477?v=4", + "profile": "https://github.com/jarquile", + "contributions": [ + "bug" + ] + }, + { + "login": "jbennett2091", + "name": "jbennett2091", + "avatar_url": "https://avatars.githubusercontent.com/u/16721671?v=4", + "profile": "https://github.com/jbennett2091", + "contributions": [ + "bug" + ] + }, + { + "login": "jborgers", + "name": "Jeroen Borgers", + "avatar_url": "https://avatars.githubusercontent.com/u/24591067?v=4", + "profile": "https://github.com/jborgers", + "contributions": [ + "bug" + ] + }, + { + "login": "jcamerin", + "name": "jcamerin", + "avatar_url": "https://avatars.githubusercontent.com/u/7663252?v=4", + "profile": "https://github.com/jcamerin", + "contributions": [ + "bug" + ] + }, + { + "login": "jdwill", + "name": "Jason Williams", + "avatar_url": "https://avatars.githubusercontent.com/u/7173290?v=4", + "profile": "https://github.com/jdwill", + "contributions": [ + "bug" + ] + }, + { + "login": "jeffjensen", + "name": "Jeff Jensen", + "avatar_url": "https://avatars.githubusercontent.com/u/63805?v=4", + "profile": "https://github.com/jeffjensen", + "contributions": [ + "bug" + ] + }, + { + "login": "JeffMTI", + "name": "Jeff May", + "avatar_url": "https://avatars.githubusercontent.com/u/8986912?v=4", + "profile": "https://github.com/JeffMTI", + "contributions": [ + "bug" + ] + }, + { + "login": "jensgerdes", + "name": "Jens Gerdes", + "avatar_url": "https://avatars.githubusercontent.com/u/30986864?v=4", + "profile": "https://dpa.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "jerome-d-russ", + "name": "Jerome Russ", + "avatar_url": "https://avatars.githubusercontent.com/u/10404699?v=4", + "profile": "https://github.com/jerome-d-russ", + "contributions": [ + "bug" + ] + }, + { + "login": "jgerken", + "name": "Jan", + "avatar_url": "https://avatars.githubusercontent.com/u/1132816?v=4", + "profile": "https://github.com/jgerken", + "contributions": [ + "bug" + ] + }, + { + "login": "jiangty-addepar", + "name": "Damien Jiang", + "avatar_url": "https://avatars.githubusercontent.com/u/4613397?v=4", + "profile": "https://github.com/jiangty-addepar", + "contributions": [ + "bug" + ] + }, + { + "login": "jiri-pejchal", + "name": "Jiri Pejchal", + "avatar_url": "https://avatars.githubusercontent.com/u/1450087?v=4", + "profile": "https://github.com/jiri-pejchal", + "contributions": [ + "bug" + ] + }, + { + "login": "JiriSko", + "name": "Jiří Škorpil", + "avatar_url": "https://avatars.githubusercontent.com/u/997061?v=4", + "profile": "https://github.com/JiriSko", + "contributions": [ + "bug" + ] + }, + { + "login": "jithinqburst", + "name": "Jithin Sunny", + "avatar_url": "https://avatars.githubusercontent.com/u/15030774?v=4", + "profile": "https://github.com/jithinqburst", + "contributions": [ + "bug" + ] + }, + { + "login": "JJengility", + "name": "JJengility", + "avatar_url": "https://avatars.githubusercontent.com/u/29776644?v=4", + "profile": "https://github.com/JJengility", + "contributions": [ + "bug" + ] + }, + { + "login": "jjlharrison", + "name": "James Harrison", + "avatar_url": "https://avatars.githubusercontent.com/u/242337?v=4", + "profile": "https://github.com/jjlharrison", + "contributions": [ + "bug" + ] + }, + { + "login": "jkeener1", + "name": "jkeener1", + "avatar_url": "https://avatars.githubusercontent.com/u/11696155?v=4", + "profile": "https://github.com/jkeener1", + "contributions": [ + "bug" + ] + }, + { + "login": "jlelse", + "name": "Jan-Lukas Else", + "avatar_url": "https://avatars.githubusercontent.com/u/8822316?v=4", + "profile": "https://jlelse.dev/", + "contributions": [ + "bug" + ] + }, + { + "login": "jmetertea", + "name": "jmetertea", + "avatar_url": "https://avatars.githubusercontent.com/u/33323555?v=4", + "profile": "https://github.com/jmetertea", + "contributions": [ + "bug" + ] + }, + { + "login": "jogu", + "name": "Joseph Heenan", + "avatar_url": "https://avatars.githubusercontent.com/u/316456?v=4", + "profile": "https://github.com/jogu", + "contributions": [ + "bug" + ] + }, + { + "login": "johanhammar", + "name": "Johan Hammar", + "avatar_url": "https://avatars.githubusercontent.com/u/69011?v=4", + "profile": "http://about.me/johanhammar/", + "contributions": [ + "bug" + ] + }, + { + "login": "john3300", + "name": "Brian Johnson", + "avatar_url": "https://avatars.githubusercontent.com/u/10662490?v=4", + "profile": "https://github.com/john3300", + "contributions": [ + "bug" + ] + }, + { + "login": "john5f35", + "name": "John Zhang", + "avatar_url": "https://avatars.githubusercontent.com/u/3710709?v=4", + "profile": "https://au.linkedin.com/in/johnjiabinzhang", + "contributions": [ + "bug" + ] + }, + { + "login": "john-karp", + "name": "John Karp", + "avatar_url": "https://avatars.githubusercontent.com/u/6526886?v=4", + "profile": "https://github.com/john-karp", + "contributions": [ + "bug" + ] + }, + { + "login": "jonasgeiregat", + "name": "Jonas Geiregat", + "avatar_url": "https://avatars.githubusercontent.com/u/288105?v=4", + "profile": "https://github.com/jonasgeiregat", + "contributions": [ + "bug" + ] + }, + { + "login": "jordillachmrf", + "name": "Jordi Llach", + "avatar_url": "https://avatars.githubusercontent.com/u/33347279?v=4", + "profile": "https://github.com/jordillachmrf", + "contributions": [ + "bug" + ] + }, + { + "login": "JorneVL", + "name": "JorneVL", + "avatar_url": "https://avatars.githubusercontent.com/u/27012293?v=4", + "profile": "https://github.com/JorneVL", + "contributions": [ + "bug" + ] + }, + { + "login": "jorsol", + "name": "Jorge Solórzano", + "avatar_url": "https://avatars.githubusercontent.com/u/3739977?v=4", + "profile": "https://www.jorsol.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "josepalafox", + "name": "Jose Palafox", + "avatar_url": "https://avatars.githubusercontent.com/u/1505000?v=4", + "profile": "https://github.com/josepalafox", + "contributions": [ + "bug" + ] + }, + { + "login": "jslaroch", + "name": "Jean-Simon Larochelle", + "avatar_url": "https://avatars.githubusercontent.com/u/49211137?v=4", + "profile": "https://github.com/jslaroch", + "contributions": [ + "bug" + ] + }, + { + "login": "juli1", + "name": "Julien", + "avatar_url": "https://avatars.githubusercontent.com/u/993972?v=4", + "profile": "http://julien.gunnm.org/", + "contributions": [ + "bug" + ] + }, + { + "login": "julius-d", + "name": "Julius", + "avatar_url": "https://avatars.githubusercontent.com/u/30121440?v=4", + "profile": "https://github.com/julius-d", + "contributions": [ + "bug" + ] + }, + { + "login": "justgrumpy", + "name": "Brian Batronis", + "avatar_url": "https://avatars.githubusercontent.com/u/1242388?v=4", + "profile": "https://github.com/justgrumpy", + "contributions": [ + "bug" + ] + }, + { + "login": "JustPRV", + "name": "JustPRV", + "avatar_url": "https://avatars.githubusercontent.com/u/3972281?v=4", + "profile": "https://github.com/JustPRV", + "contributions": [ + "bug" + ] + }, + { + "login": "kamath-prasad", + "name": "Prasad Kamath", + "avatar_url": "https://avatars.githubusercontent.com/u/28445395?v=4", + "profile": "https://github.com/kamath-prasad", + "contributions": [ + "bug" + ] + }, + { + "login": "karwer", + "name": "karwer", + "avatar_url": "https://avatars.githubusercontent.com/u/862540?v=4", + "profile": "https://github.com/karwer", + "contributions": [ + "bug" + ] + }, + { + "login": "kaulonline", + "name": "kaulonline", + "avatar_url": "https://avatars.githubusercontent.com/u/1171723?v=4", + "profile": "https://github.com/kaulonline", + "contributions": [ + "bug" + ] + }, + { + "login": "kayoub5", + "name": "Ayoub Kaanich", + "avatar_url": "https://avatars.githubusercontent.com/u/1814900?v=4", + "profile": "https://github.com/kayoub5", + "contributions": [ + "bug" + ] + }, + { + "login": "kdaemonv", + "name": "kdaemonv", + "avatar_url": "https://avatars.githubusercontent.com/u/5984651?v=4", + "profile": "https://github.com/kdaemonv", + "contributions": [ + "bug" + ] + }, + { + "login": "kevemueller", + "name": "Keve Müller", + "avatar_url": "https://avatars.githubusercontent.com/u/15782588?v=4", + "profile": "https://github.com/kevemueller", + "contributions": [ + "bug" + ] + }, + { + "login": "kevin-wayne", + "name": "Kevin Wayne", + "avatar_url": "https://avatars.githubusercontent.com/u/5225666?v=4", + "profile": "http://www.cs.princeton.edu/~wayne", + "contributions": [ + "bug" + ] + }, + { + "login": "KevSlashNull", + "name": "Kev", + "avatar_url": "https://avatars.githubusercontent.com/u/28510368?v=4", + "profile": "https://siege.gg/", + "contributions": [ + "bug" + ] + }, + { + "login": "kfranic", + "name": "kfranic", + "avatar_url": "https://avatars.githubusercontent.com/u/26544594?v=4", + "profile": "https://github.com/kfranic", + "contributions": [ + "bug" + ] + }, + { + "login": "khalidkh", + "name": "khalidkh", + "avatar_url": "https://avatars.githubusercontent.com/u/6832066?v=4", + "profile": "https://github.com/khalidkh", + "contributions": [ + "bug" + ] + }, + { + "login": "kieranlblack", + "name": "Kieran Black", + "avatar_url": "https://avatars.githubusercontent.com/u/48463323?v=4", + "profile": "https://github.com/kieranlblack", + "contributions": [ + "bug" + ] + }, + { + "login": "KirillZubov", + "name": "Kirill Zubov", + "avatar_url": "https://avatars.githubusercontent.com/u/12683885?v=4", + "profile": "https://github.com/KirillZubov", + "contributions": [ + "bug" + ] + }, + { + "login": "kloessst", + "name": "Stefan Klöss-Schuster", + "avatar_url": "https://avatars.githubusercontent.com/u/16407766?v=4", + "profile": "https://github.com/kloessst", + "contributions": [ + "bug" + ] + }, + { + "login": "koma0277", + "name": "Manfred Koch", + "avatar_url": "https://avatars.githubusercontent.com/u/35556790?v=4", + "profile": "https://github.com/koma0277", + "contributions": [ + "bug" + ] + }, + { + "login": "krallus", + "name": "Philip Hachey", + "avatar_url": "https://avatars.githubusercontent.com/u/29927450?v=4", + "profile": "https://github.com/krallus", + "contributions": [ + "bug" + ] + }, + { + "login": "kraussjo", + "name": "Jochen Krauss", + "avatar_url": "https://avatars.githubusercontent.com/u/38663002?v=4", + "profile": "https://github.com/kraussjo", + "contributions": [ + "bug" + ] + }, + { + "login": "krichter722", + "name": "Karl-Philipp Richter", + "avatar_url": "https://avatars.githubusercontent.com/u/4369372?v=4", + "profile": "https://github.com/krichter722", + "contributions": [ + "bug" + ] + }, + { + "login": "krzyk", + "name": "krzyk", + "avatar_url": "https://avatars.githubusercontent.com/u/105730?v=4", + "profile": "https://github.com/krzyk", + "contributions": [ + "bug" + ] + }, + { + "login": "ksilz", + "name": "Karsten Silz", + "avatar_url": "https://avatars.githubusercontent.com/u/1061209?v=4", + "profile": "https://betterprojectsfaster.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "kthanky", + "name": "Kunal Thanki", + "avatar_url": "https://avatars.githubusercontent.com/u/13259828?v=4", + "profile": "https://github.com/kthanky", + "contributions": [ + "bug" + ] + }, + { + "login": "KThompso", + "name": "KThompso", + "avatar_url": "https://avatars.githubusercontent.com/u/2643885?v=4", + "profile": "https://github.com/KThompso", + "contributions": [ + "bug" + ] + }, + { + "login": "kuanrongdelvdou", + "name": "Eden Hao", + "avatar_url": "https://avatars.githubusercontent.com/u/33919823?v=4", + "profile": "https://github.com/kuanrongdelvdou", + "contributions": [ + "bug" + ] + }, + { + "login": "kullfar", + "name": "Stanislav Gromov", + "avatar_url": "https://avatars.githubusercontent.com/u/736714?v=4", + "profile": "https://github.com/kullfar", + "contributions": [ + "bug" + ] + }, + { + "login": "l0s", + "name": "Carlos Macasaet", + "avatar_url": "https://avatars.githubusercontent.com/u/210451?v=4", + "profile": "https://github.com/l0s", + "contributions": [ + "bug" + ] + }, + { + "login": "lars-sh", + "name": "Lars Knickrehm", + "avatar_url": "https://avatars.githubusercontent.com/u/880198?v=4", + "profile": "https://lars-sh.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "lasselindqvist", + "name": "lasselindqvist", + "avatar_url": "https://avatars.githubusercontent.com/u/13466645?v=4", + "profile": "https://github.com/lasselindqvist", + "contributions": [ + "bug" + ] + }, + { + "login": "ledoyen", + "name": "Loïc Ledoyen", + "avatar_url": "https://avatars.githubusercontent.com/u/6298315?v=4", + "profile": "https://github.com/ledoyen", + "contributions": [ + "bug" + ] + }, + { + "login": "lgabrielgr", + "name": "Leo Gutierrez", + "avatar_url": "https://avatars.githubusercontent.com/u/760959?v=4", + "profile": "https://github.com/lgabrielgr", + "contributions": [ + "bug" + ] + }, + { + "login": "lgoldstein", + "name": "Lyor Goldstein", + "avatar_url": "https://avatars.githubusercontent.com/u/1436205?v=4", + "profile": "https://github.com/lgoldstein", + "contributions": [ + "bug" + ] + }, + { + "login": "lidel", + "name": "Marcin Rataj", + "avatar_url": "https://avatars.githubusercontent.com/u/157609?v=4", + "profile": "https://lidel.org/", + "contributions": [ + "bug" + ] + }, + { + "login": "lihuaib", + "name": "lihuaib", + "avatar_url": "https://avatars.githubusercontent.com/u/3365643?v=4", + "profile": "https://github.com/lihuaib", + "contributions": [ + "bug" + ] + }, + { + "login": "Lintsi", + "name": "Lintsi", + "avatar_url": "https://avatars.githubusercontent.com/u/6848650?v=4", + "profile": "https://github.com/Lintsi", + "contributions": [ + "bug" + ] + }, + { + "login": "LixonLookose", + "name": "Lixon Lookose", + "avatar_url": "https://avatars.githubusercontent.com/u/66419481?v=4", + "profile": "https://github.com/LixonLookose", + "contributions": [ + "bug" + ] + }, + { + "login": "logesh14", + "name": "Logesh", + "avatar_url": "https://avatars.githubusercontent.com/u/30902439?v=4", + "profile": "https://github.com/logesh14", + "contributions": [ + "bug" + ] + }, + { + "login": "lolgab", + "name": "Lorenzo Gabriele", + "avatar_url": "https://avatars.githubusercontent.com/u/5793054?v=4", + "profile": "https://github.com/lolgab", + "contributions": [ + "bug" + ] + }, + { + "login": "lonelyma1021", + "name": "lonelyma1021", + "avatar_url": "https://avatars.githubusercontent.com/u/22359014?v=4", + "profile": "https://github.com/lonelyma1021", + "contributions": [ + "bug" + ] + }, + { + "login": "lpeddy", + "name": "lpeddy", + "avatar_url": "https://avatars.githubusercontent.com/u/48803108?v=4", + "profile": "https://github.com/lpeddy", + "contributions": [ + "bug" + ] + }, + { + "login": "lslonina", + "name": "Lukasz Slonina", + "avatar_url": "https://avatars.githubusercontent.com/u/12303865?v=4", + "profile": "https://github.com/lslonina", + "contributions": [ + "bug" + ] + }, + { + "login": "lssilva", + "name": "Lucas Silva", + "avatar_url": "https://avatars.githubusercontent.com/u/7464888?v=4", + "profile": "https://github.com/lssilva", + "contributions": [ + "bug" + ] + }, + { + "login": "Lukebray", + "name": "Lukebray", + "avatar_url": "https://avatars.githubusercontent.com/u/39488446?v=4", + "profile": "https://github.com/Lukebray", + "contributions": [ + "bug" + ] + }, + { + "login": "lyriccoder", + "name": "lyriccoder", + "avatar_url": "https://avatars.githubusercontent.com/u/20803206?v=4", + "profile": "https://github.com/lyriccoder", + "contributions": [ + "bug" + ] + }, + { + "login": "Macarse", + "name": "Macarse", + "avatar_url": "https://avatars.githubusercontent.com/u/24915?v=4", + "profile": "https://github.com/Macarse", + "contributions": [ + "bug" + ] + }, + { + "login": "machadoit", + "name": "Joao Machado", + "avatar_url": "https://avatars.githubusercontent.com/u/13315199?v=4", + "profile": "https://github.com/machadoit", + "contributions": [ + "bug" + ] + }, + { + "login": "maggu2810", + "name": "Markus Rathgeb", + "avatar_url": "https://avatars.githubusercontent.com/u/204670?v=4", + "profile": "https://github.com/maggu2810", + "contributions": [ + "bug" + ] + }, + { + "login": "magwas", + "name": "Árpád Magosányi", + "avatar_url": "https://avatars.githubusercontent.com/u/756838?v=4", + "profile": "https://github.com/magwas", + "contributions": [ + "bug" + ] + }, + { + "login": "maksim-m", + "name": "Maksim Moiseikin", + "avatar_url": "https://avatars.githubusercontent.com/u/1863269?v=4", + "profile": "https://github.com/maksim-m", + "contributions": [ + "bug" + ] + }, + { + "login": "malejpavouk", + "name": "Pavel Mička", + "avatar_url": "https://avatars.githubusercontent.com/u/4127023?v=4", + "profile": "https://github.com/malejpavouk", + "contributions": [ + "bug" + ] + }, + { + "login": "marcelhaerle", + "name": "Marcel Härle", + "avatar_url": "https://avatars.githubusercontent.com/u/5338817?v=4", + "profile": "https://github.com/marcelhaerle", + "contributions": [ + "bug" + ] + }, + { + "login": "marcello-fialho", + "name": "Marcello Fialho", + "avatar_url": "https://avatars.githubusercontent.com/u/28719666?v=4", + "profile": "https://github.com/marcello-fialho", + "contributions": [ + "bug" + ] + }, + { + "login": "marcelmore", + "name": "marcelmore", + "avatar_url": "https://avatars.githubusercontent.com/u/2975481?v=4", + "profile": "https://github.com/marcelmore", + "contributions": [ + "bug" + ] + }, + { + "login": "markkolich", + "name": "Mark Kolich", + "avatar_url": "https://avatars.githubusercontent.com/u/1202420?v=4", + "profile": "https://mark.koli.ch/", + "contributions": [ + "bug" + ] + }, + { + "login": "markpritchard", + "name": "Mark Pritchard", + "avatar_url": "https://avatars.githubusercontent.com/u/8234070?v=4", + "profile": "https://github.com/markpritchard", + "contributions": [ + "bug" + ] + }, + { + "login": "marquiswang", + "name": "Marquis Wang", + "avatar_url": "https://avatars.githubusercontent.com/u/358220?v=4", + "profile": "http://marquiswang.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "Martin-Spamer", + "name": "Martin Spamer", + "avatar_url": "https://avatars.githubusercontent.com/u/2852862?v=4", + "profile": "http://www.spamer.me.uk/", + "contributions": [ + "bug" + ] + }, + { + "login": "martin-tarjanyi", + "name": "Martin Tarjányi", + "avatar_url": "https://avatars.githubusercontent.com/u/17810336?v=4", + "profile": "https://github.com/martin-tarjanyi", + "contributions": [ + "bug" + ] + }, + { + "login": "martofeld", + "name": "Martin Feldsztejn", + "avatar_url": "https://avatars.githubusercontent.com/u/5756343?v=4", + "profile": "https://github.com/martofeld", + "contributions": [ + "bug" + ] + }, + { + "login": "matchboxy", + "name": "matchbox", + "avatar_url": "https://avatars.githubusercontent.com/u/6457674?v=4", + "profile": "https://github.com/matchboxy", + "contributions": [ + "bug" + ] + }, + { + "login": "mateusz-stefanski", + "name": "Mateusz Stefanski", + "avatar_url": "https://avatars.githubusercontent.com/u/28163508?v=4", + "profile": "https://github.com/mateusz-stefanski", + "contributions": [ + "bug" + ] + }, + { + "login": "MatFl", + "name": "MatFl", + "avatar_url": "https://avatars.githubusercontent.com/u/8408624?v=4", + "profile": "https://github.com/MatFl", + "contributions": [ + "bug" + ] + }, + { + "login": "mathieugouin", + "name": "Mathieu Gouin", + "avatar_url": "https://avatars.githubusercontent.com/u/11562302?v=4", + "profile": "https://github.com/mathieugouin", + "contributions": [ + "bug" + ] + }, + { + "login": "matthiaskraaz", + "name": "matthiaskraaz", + "avatar_url": "https://avatars.githubusercontent.com/u/5954500?v=4", + "profile": "https://github.com/matthiaskraaz", + "contributions": [ + "bug" + ] + }, + { + "login": "mattnelson", + "name": "Matt Nelson", + "avatar_url": "https://avatars.githubusercontent.com/u/1894657?v=4", + "profile": "https://github.com/mattnelson", + "contributions": [ + "bug" + ] + }, + { + "login": "mayerj", + "name": "Jean-Paul Mayer", + "avatar_url": "https://avatars.githubusercontent.com/u/4032461?v=4", + "profile": "https://github.com/mayerj", + "contributions": [ + "bug" + ] + }, + { + "login": "mbenson", + "name": "Matt Benson", + "avatar_url": "https://avatars.githubusercontent.com/u/487462?v=4", + "profile": "https://github.com/mbenson", + "contributions": [ + "bug" + ] + }, + { + "login": "mcandre", + "name": "Andrew", + "avatar_url": "https://avatars.githubusercontent.com/u/5316?v=4", + "profile": "https://github.com/mcandre", + "contributions": [ + "bug" + ] + }, + { + "login": "mclay", + "name": "Michael Clay", + "avatar_url": "https://avatars.githubusercontent.com/u/393727?v=4", + "profile": "https://github.com/mclay", + "contributions": [ + "bug" + ] + }, + { + "login": "MCMicS", + "name": "MCMicS", + "avatar_url": "https://avatars.githubusercontent.com/u/4604206?v=4", + "profile": "https://mcmics.jnet24.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "mduggan", + "name": "Matthew Duggan", + "avatar_url": "https://avatars.githubusercontent.com/u/3765590?v=4", + "profile": "https://github.com/mduggan", + "contributions": [ + "bug" + ] + }, + { + "login": "megascus", + "name": "Satoshi Kubo", + "avatar_url": "https://avatars.githubusercontent.com/u/976085?v=4", + "profile": "http://d.hatena.ne.jp/megascus/", + "contributions": [ + "bug" + ] + }, + { + "login": "MessShawn", + "name": "Simon Xiao", + "avatar_url": "https://avatars.githubusercontent.com/u/3095801?v=4", + "profile": "https://github.com/MessShawn", + "contributions": [ + "bug" + ] + }, + { + "login": "mhankus", + "name": "Mirek Hankus", + "avatar_url": "https://avatars.githubusercontent.com/u/6095361?v=4", + "profile": "https://github.com/mhankus", + "contributions": [ + "bug" + ] + }, + { + "login": "michaelboyles", + "name": "Michael", + "avatar_url": "https://avatars.githubusercontent.com/u/17732072?v=4", + "profile": "https://boyl.es/", + "contributions": [ + "bug" + ] + }, + { + "login": "michaelhoefer", + "name": "Michael Hoefer", + "avatar_url": "https://avatars.githubusercontent.com/u/479449?v=4", + "profile": "https://github.com/michaelhoefer", + "contributions": [ + "bug" + ] + }, + { + "login": "michalborek", + "name": "Michał Borek", + "avatar_url": "https://avatars.githubusercontent.com/u/986194?v=4", + "profile": "http://www.greenpath.pl/", + "contributions": [ + "bug" + ] + }, + { + "login": "mikebell90", + "name": "Michael Bell", + "avatar_url": "https://avatars.githubusercontent.com/u/780301?v=4", + "profile": "https://github.com/mikebell90", + "contributions": [ + "bug" + ] + }, + { + "login": "MikeDombo", + "name": "Michael Dombrowski", + "avatar_url": "https://avatars.githubusercontent.com/u/3926405?v=4", + "profile": "https://mikedombrowski.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "mikesive", + "name": "mikesive", + "avatar_url": "https://avatars.githubusercontent.com/u/4043189?v=4", + "profile": "https://github.com/mikesive", + "contributions": [ + "bug" + ] + }, + { + "login": "milossesic", + "name": "milossesic", + "avatar_url": "https://avatars.githubusercontent.com/u/20756244?v=4", + "profile": "https://github.com/milossesic", + "contributions": [ + "bug" + ] + }, + { + "login": "miPlodder", + "name": "Saksham Handu", + "avatar_url": "https://avatars.githubusercontent.com/u/22195621?v=4", + "profile": "https://miplodder.github.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "mistercam", + "name": "Cameron Donaldson", + "avatar_url": "https://avatars.githubusercontent.com/u/124565?v=4", + "profile": "https://github.com/mistercam", + "contributions": [ + "bug" + ] + }, + { + "login": "MisterSquishy", + "name": "Pete Davids", + "avatar_url": "https://avatars.githubusercontent.com/u/5599894?v=4", + "profile": "https://github.com/MisterSquishy", + "contributions": [ + "bug" + ] + }, + { + "login": "mkeller-ergon", + "name": "meandonlyme", + "avatar_url": "https://avatars.githubusercontent.com/u/23031669?v=4", + "profile": "https://github.com/mkeller-ergon", + "contributions": [ + "bug" + ] + }, + { + "login": "mkordas", + "name": "Michal Kordas", + "avatar_url": "https://avatars.githubusercontent.com/u/5467276?v=4", + "profile": "http://pl.linkedin.com/in/mkordas/", + "contributions": [ + "bug" + ] + }, + { + "login": "mnlipp", + "name": "Michael N. Lipp", + "avatar_url": "https://avatars.githubusercontent.com/u/1446020?v=4", + "profile": "https://mnlipp.github.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "mnunezdm", + "name": "Miguel Núñez Díaz-Montes", + "avatar_url": "https://avatars.githubusercontent.com/u/10410852?v=4", + "profile": "https://mnunezdm.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "mpellegrini", + "name": "Michael Pellegrini", + "avatar_url": "https://avatars.githubusercontent.com/u/466696?v=4", + "profile": "https://github.com/mpellegrini", + "contributions": [ + "bug" + ] + }, + { + "login": "MPoorter", + "name": "Matt De Poorter", + "avatar_url": "https://avatars.githubusercontent.com/u/25356097?v=4", + "profile": "https://github.com/MPoorter", + "contributions": [ + "bug" + ] + }, + { + "login": "MrAngry52", + "name": "MrAngry52", + "avatar_url": "https://avatars.githubusercontent.com/u/30026386?v=4", + "profile": "https://github.com/MrAngry52", + "contributions": [ + "bug" + ] + }, + { + "login": "mrb", + "name": "Michael Bernstein", + "avatar_url": "https://avatars.githubusercontent.com/u/2878?v=4", + "profile": "http://reifyworks.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "mrfyda", + "name": "Rafael Cortês", + "avatar_url": "https://avatars.githubusercontent.com/u/593860?v=4", + "profile": "https://github.com/mrfyda", + "contributions": [ + "bug" + ] + }, + { + "login": "mriddell95", + "name": "mriddell95", + "avatar_url": "https://avatars.githubusercontent.com/u/25618660?v=4", + "profile": "https://github.com/mriddell95", + "contributions": [ + "bug" + ] + }, + { + "login": "mrlzh", + "name": "mrlzh", + "avatar_url": "https://avatars.githubusercontent.com/u/13222791?v=4", + "profile": "https://github.com/mrlzh", + "contributions": [ + "bug" + ] + }, + { + "login": "mryan43", + "name": "Manuel Ryan", + "avatar_url": "https://avatars.githubusercontent.com/u/223869?v=4", + "profile": "https://github.com/mryan43", + "contributions": [ + "bug" + ] + }, + { + "login": "msiemczyk", + "name": "Maciek Siemczyk", + "avatar_url": "https://avatars.githubusercontent.com/u/5693250?v=4", + "profile": "https://github.com/msiemczyk", + "contributions": [ + "bug" + ] + }, + { + "login": "msloan", + "name": "msloan", + "avatar_url": "https://avatars.githubusercontent.com/u/1783723?v=4", + "profile": "https://github.com/msloan", + "contributions": [ + "bug" + ] + }, + { + "login": "mucharlaravalika", + "name": "mucharlaravalika", + "avatar_url": "https://avatars.githubusercontent.com/u/32505587?v=4", + "profile": "https://github.com/mucharlaravalika", + "contributions": [ + "bug" + ] + }, + { + "login": "muminc", + "name": "Muminur Choudhury", + "avatar_url": "https://avatars.githubusercontent.com/u/934067?v=4", + "profile": "https://github.com/muminc", + "contributions": [ + "bug" + ] + }, + { + "login": "mvenneman", + "name": "mvenneman", + "avatar_url": "https://avatars.githubusercontent.com/u/1266912?v=4", + "profile": "https://github.com/mvenneman", + "contributions": [ + "bug" + ] + }, + { + "login": "mvitaly", + "name": "Vitaly Polonetsky", + "avatar_url": "https://avatars.githubusercontent.com/u/275446?v=4", + "profile": "http://www.topixoft.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "Myllyenko", + "name": "Igor Melnichenko", + "avatar_url": "https://avatars.githubusercontent.com/u/10358254?v=4", + "profile": "https://github.com/Myllyenko", + "contributions": [ + "bug" + ] + }, + { + "login": "Nagendra080389", + "name": "Nagendra Kumar Singh", + "avatar_url": "https://avatars.githubusercontent.com/u/6088582?v=4", + "profile": "https://github.com/Nagendra080389", + "contributions": [ + "bug" + ] + }, + { + "login": "nalexn", + "name": "Alexey Naumov", + "avatar_url": "https://avatars.githubusercontent.com/u/1594746?v=4", + "profile": "https://nalexn.github.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "nareshl119", + "name": "nareshl119", + "avatar_url": "https://avatars.githubusercontent.com/u/39321364?v=4", + "profile": "https://github.com/nareshl119", + "contributions": [ + "bug" + ] + }, + { + "login": "nathanspectacular", + "name": "Nathan Reynolds", + "avatar_url": "https://avatars.githubusercontent.com/u/88563519?v=4", + "profile": "https://github.com/nathanspectacular", + "contributions": [ + "bug" + ] + }, + { + "login": "nawforce", + "name": "Kevin Jones", + "avatar_url": "https://avatars.githubusercontent.com/u/25647167?v=4", + "profile": "https://github.com/nawforce", + "contributions": [ + "bug" + ] + }, + { + "login": "Nazdravi", + "name": "Nazdravi", + "avatar_url": "https://avatars.githubusercontent.com/u/273093?v=4", + "profile": "https://github.com/Nazdravi", + "contributions": [ + "bug" + ] + }, + { + "login": "nbraun-Google", + "name": "Nathan Braun", + "avatar_url": "https://avatars.githubusercontent.com/u/52723353?v=4", + "profile": "https://github.com/nbraun-Google", + "contributions": [ + "bug" + ] + }, + { + "login": "nchursin", + "name": "Nikita Chursin", + "avatar_url": "https://avatars.githubusercontent.com/u/8916229?v=4", + "profile": "https://www.salesforcecraft.dev/", + "contributions": [ + "bug" + ] + }, + { + "login": "Neha-Dhonde", + "name": "Neha-Dhonde", + "avatar_url": "https://avatars.githubusercontent.com/u/31505059?v=4", + "profile": "https://github.com/Neha-Dhonde", + "contributions": [ + "bug" + ] + }, + { + "login": "NickButcher1", + "name": "Nick Butcher", + "avatar_url": "https://avatars.githubusercontent.com/u/8671565?v=4", + "profile": "https://github.com/NickButcher1", + "contributions": [ + "bug" + ] + }, + { + "login": "nicoabie", + "name": "Nico Gallinal", + "avatar_url": "https://avatars.githubusercontent.com/u/2797992?v=4", + "profile": "https://github.com/nicoabie", + "contributions": [ + "bug" + ] + }, + { + "login": "nicolas-harraudeau-sonarsource", + "name": "nicolas-harraudeau-sonarsource", + "avatar_url": "https://avatars.githubusercontent.com/u/40498978?v=4", + "profile": "https://github.com/nicolas-harraudeau-sonarsource", + "contributions": [ + "bug" + ] + }, + { + "login": "Nightfirecat", + "name": "Jordan", + "avatar_url": "https://avatars.githubusercontent.com/u/2199511?v=4", + "profile": "https://nightfirec.at/", + "contributions": [ + "bug" + ] + }, + { + "login": "niktekusho", + "name": "Nicola Dal Maso", + "avatar_url": "https://avatars.githubusercontent.com/u/18280135?v=4", + "profile": "https://github.com/niktekusho", + "contributions": [ + "bug" + ] + }, + { + "login": "NileshVirkar", + "name": "Nilesh Virkar", + "avatar_url": "https://avatars.githubusercontent.com/u/24671786?v=4", + "profile": "https://nileshvirkar.github.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "Nimfadora", + "name": "Valeria", + "avatar_url": "https://avatars.githubusercontent.com/u/10544767?v=4", + "profile": "https://github.com/Nimfadora", + "contributions": [ + "bug" + ] + }, + { + "login": "nimit-patel", + "name": "Nimit Patel", + "avatar_url": "https://avatars.githubusercontent.com/u/13987001?v=4", + "profile": "https://github.com/nimit-patel", + "contributions": [ + "bug" + ] + }, + { + "login": "niranjanh", + "name": "Niranjan Harpale", + "avatar_url": "https://avatars.githubusercontent.com/u/23009945?v=4", + "profile": "https://github.com/niranjanh", + "contributions": [ + "bug" + ] + }, + { + "login": "njdoyle", + "name": "Nicholas Doyle", + "avatar_url": "https://avatars.githubusercontent.com/u/316852?v=4", + "profile": "https://github.com/njdoyle", + "contributions": [ + "bug" + ] + }, + { + "login": "Noah0120", + "name": "Noah0120", + "avatar_url": "https://avatars.githubusercontent.com/u/86766856?v=4", + "profile": "https://github.com/Noah0120", + "contributions": [ + "bug" + ] + }, + { + "login": "noahsussman", + "name": "Noah Sussman", + "avatar_url": "https://avatars.githubusercontent.com/u/31490710?v=4", + "profile": "https://github.com/noahsussman", + "contributions": [ + "bug" + ] + }, + { + "login": "noamtamim", + "name": "Noam Tamim", + "avatar_url": "https://avatars.githubusercontent.com/u/10047237?v=4", + "profile": "https://noamtamim.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "noerremark", + "name": "noerremark", + "avatar_url": "https://avatars.githubusercontent.com/u/4252411?v=4", + "profile": "https://github.com/noerremark", + "contributions": [ + "bug" + ] + }, + { + "login": "novsirion", + "name": "novsirion", + "avatar_url": "https://avatars.githubusercontent.com/u/7797113?v=4", + "profile": "https://github.com/novsirion", + "contributions": [ + "bug" + ] + }, + { + "login": "numeralnathan", + "name": "Nathan Reynolds", + "avatar_url": "https://avatars.githubusercontent.com/u/1236594?v=4", + "profile": "https://github.com/numeralnathan", + "contributions": [ + "bug" + ] + }, + { + "login": "oggboy", + "name": "oggboy", + "avatar_url": "https://avatars.githubusercontent.com/u/4798818?v=4", + "profile": "https://github.com/oggboy", + "contributions": [ + "bug" + ] + }, + { + "login": "oinume", + "name": "oinume", + "avatar_url": "https://avatars.githubusercontent.com/u/78990?v=4", + "profile": "https://journal.lampetty.net/archive/category/in%20English", + "contributions": [ + "bug" + ] + }, + { + "login": "oitsjustjose", + "name": "Jose Stovall", + "avatar_url": "https://avatars.githubusercontent.com/u/3887324?v=4", + "profile": "https://oitsjustjose.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "OlafHaalstra", + "name": "Olaf Haalstra", + "avatar_url": "https://avatars.githubusercontent.com/u/6420723?v=4", + "profile": "https://github.com/OlafHaalstra", + "contributions": [ + "bug" + ] + }, + { + "login": "Oleg-Pavlenko-EPAM", + "name": "Oleg Pavlenko", + "avatar_url": "https://avatars.githubusercontent.com/u/82513963?v=4", + "profile": "https://github.com/Oleg-Pavlenko-EPAM", + "contributions": [ + "bug" + ] + }, + { + "login": "osek666", + "name": "Artur Ossowski", + "avatar_url": "https://avatars.githubusercontent.com/u/676655?v=4", + "profile": "https://github.com/osek666", + "contributions": [ + "bug" + ] + }, + { + "login": "OverDrone", + "name": "OverDrone", + "avatar_url": "https://avatars.githubusercontent.com/u/8506029?v=4", + "profile": "https://github.com/OverDrone", + "contributions": [ + "bug" + ] + }, + { + "login": "pagarwal-ignitetech", + "name": "pallavi agarwal", + "avatar_url": "https://avatars.githubusercontent.com/u/30888430?v=4", + "profile": "https://github.com/pagarwal-ignitetech", + "contributions": [ + "bug" + ] + }, + { + "login": "pandaadb", + "name": "Artur", + "avatar_url": "https://avatars.githubusercontent.com/u/15109364?v=4", + "profile": "https://github.com/pandaadb", + "contributions": [ + "bug" + ] + }, + { + "login": "PaperTsar", + "name": "Bendegúz Nagy", + "avatar_url": "https://avatars.githubusercontent.com/u/17526848?v=4", + "profile": "https://github.com/PaperTsar", + "contributions": [ + "bug" + ] + }, + { + "login": "parbatiSF", + "name": "Parbati Bose", + "avatar_url": "https://avatars.githubusercontent.com/u/37078591?v=4", + "profile": "https://github.com/parbatiSF", + "contributions": [ + "bug" + ] + }, + { + "login": "parksungrin", + "name": "parksungrin", + "avatar_url": "https://avatars.githubusercontent.com/u/29750262?v=4", + "profile": "https://github.com/parksungrin", + "contributions": [ + "bug" + ] + }, + { + "login": "patpatpat123", + "name": "patpatpat123", + "avatar_url": "https://avatars.githubusercontent.com/u/43899031?v=4", + "profile": "https://github.com/patpatpat123", + "contributions": [ + "bug" + ] + }, + { + "login": "patriksevallius", + "name": "patriksevallius", + "avatar_url": "https://avatars.githubusercontent.com/u/7291479?v=4", + "profile": "https://github.com/patriksevallius", + "contributions": [ + "bug" + ] + }, + { + "login": "paulberg", + "name": "Paul Berg", + "avatar_url": "https://avatars.githubusercontent.com/u/3239883?v=4", + "profile": "https://github.com/paulberg", + "contributions": [ + "bug" + ] + }, + { + "login": "pbludov", + "name": "Pavel Bludov", + "avatar_url": "https://avatars.githubusercontent.com/u/900805?v=4", + "profile": "https://github.com/pbludov", + "contributions": [ + "bug" + ] + }, + { + "login": "pbrajesh1", + "name": "pbrajesh1", + "avatar_url": "https://avatars.githubusercontent.com/u/32388299?v=4", + "profile": "https://github.com/pbrajesh1", + "contributions": [ + "bug" + ] + }, + { + "login": "pdhung3012", + "name": "Hung PHAN", + "avatar_url": "https://avatars.githubusercontent.com/u/5716541?v=4", + "profile": "https://github.com/pdhung3012", + "contributions": [ + "bug" + ] + }, + { + "login": "pedrorijo91", + "name": "Pedro Rijo", + "avatar_url": "https://avatars.githubusercontent.com/u/1999050?v=4", + "profile": "http://pedrorijo.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "petercudmore", + "name": "Peter Cudmore", + "avatar_url": "https://avatars.githubusercontent.com/u/12710815?v=4", + "profile": "https://github.com/petercudmore", + "contributions": [ + "bug" + ] + }, + { + "login": "phinehasz", + "name": "Phinehas Artemix", + "avatar_url": "https://avatars.githubusercontent.com/u/36982629?v=4", + "profile": "https://github.com/phinehasz", + "contributions": [ + "bug" + ] + }, + { + "login": "phoenix384", + "name": "phoenix384", + "avatar_url": "https://avatars.githubusercontent.com/u/3883662?v=4", + "profile": "https://github.com/phoenix384", + "contributions": [ + "bug" + ] + }, + { + "login": "PJ17101", + "name": "PUNEET JAIN", + "avatar_url": "https://avatars.githubusercontent.com/u/31703044?v=4", + "profile": "https://github.com/PJ17101", + "contributions": [ + "bug" + ] + }, + { + "login": "pkasson", + "name": "Peter Kasson", + "avatar_url": "https://avatars.githubusercontent.com/u/3072247?v=4", + "profile": "https://github.com/pkasson", + "contributions": [ + "bug" + ] + }, + { + "login": "plan3d", + "name": "plan3d", + "avatar_url": "https://avatars.githubusercontent.com/u/76825073?v=4", + "profile": "https://github.com/plan3d", + "contributions": [ + "bug" + ] + }, + { + "login": "pnsantos", + "name": "Pedro Nuno Santos", + "avatar_url": "https://avatars.githubusercontent.com/u/630567?v=4", + "profile": "https://github.com/pnsantos", + "contributions": [ + "bug" + ] + }, + { + "login": "Pontesegger", + "name": "Christian Pontesegger", + "avatar_url": "https://avatars.githubusercontent.com/u/2060477?v=4", + "profile": "https://github.com/Pontesegger", + "contributions": [ + "bug" + ] + }, + { + "login": "poojasix", + "name": "poojasix", + "avatar_url": "https://avatars.githubusercontent.com/u/85337280?v=4", + "profile": "https://github.com/poojasix", + "contributions": [ + "bug" + ] + }, + { + "login": "posto", + "name": "Dumitru Postoronca", + "avatar_url": "https://avatars.githubusercontent.com/u/899189?v=4", + "profile": "https://github.com/posto", + "contributions": [ + "bug" + ] + }, + { + "login": "pozil", + "name": "Philippe Ozil", + "avatar_url": "https://avatars.githubusercontent.com/u/5071767?v=4", + "profile": "https://pozil.github.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "prabhushrikant", + "name": "prabhushrikant", + "avatar_url": "https://avatars.githubusercontent.com/u/6848200?v=4", + "profile": "https://github.com/prabhushrikant", + "contributions": [ + "bug" + ] + }, + { + "login": "pranayjswl007", + "name": "Pranay Jaiswal", + "avatar_url": "https://avatars.githubusercontent.com/u/1728493?v=4", + "profile": "https://twitter.com/pranayjaiswal", + "contributions": [ + "bug" + ] + }, + { + "login": "Prasanna-Loga", + "name": "Prasanna", + "avatar_url": "https://avatars.githubusercontent.com/u/36530081?v=4", + "profile": "https://github.com/Prasanna-Loga", + "contributions": [ + "bug" + ] + }, + { + "login": "Presh-AR", + "name": "Presh-AR", + "avatar_url": "https://avatars.githubusercontent.com/u/20354066?v=4", + "profile": "https://github.com/Presh-AR", + "contributions": [ + "bug" + ] + }, + { + "login": "pujitha8783", + "name": "pujitha8783", + "avatar_url": "https://avatars.githubusercontent.com/u/20646357?v=4", + "profile": "https://github.com/pujitha8783", + "contributions": [ + "bug" + ] + }, + { + "login": "Puneet1726", + "name": "Puneet1726", + "avatar_url": "https://avatars.githubusercontent.com/u/26565880?v=4", + "profile": "https://github.com/Puneet1726", + "contributions": [ + "bug" + ] + }, + { + "login": "qualidafial", + "name": "Matthew Hall", + "avatar_url": "https://avatars.githubusercontent.com/u/38629?v=4", + "profile": "https://github.com/qualidafial", + "contributions": [ + "bug" + ] + }, + { + "login": "raghujayjunk", + "name": "raghujayjunk", + "avatar_url": "https://avatars.githubusercontent.com/u/48074475?v=4", + "profile": "https://github.com/raghujayjunk", + "contributions": [ + "bug" + ] + }, + { + "login": "RaheemShaik999", + "name": "RaheemShaik999", + "avatar_url": "https://avatars.githubusercontent.com/u/43146735?v=4", + "profile": "https://github.com/RaheemShaik999", + "contributions": [ + "bug" + ] + }, + { + "login": "rajeshveera", + "name": "rajeshveera", + "avatar_url": "https://avatars.githubusercontent.com/u/1306514?v=4", + "profile": "https://github.com/rajeshveera", + "contributions": [ + "bug" + ] + }, + { + "login": "rajeswarreddy88", + "name": "rajeswarreddy88", + "avatar_url": "https://avatars.githubusercontent.com/u/48543250?v=4", + "profile": "https://github.com/rajeswarreddy88", + "contributions": [ + "bug" + ] + }, + { + "login": "rajiff", + "name": "Basavaraj K N", + "avatar_url": "https://avatars.githubusercontent.com/u/1080415?v=4", + "profile": "https://github.com/rajiff", + "contributions": [ + "bug" + ] + }, + { + "login": "ramachandra-mohan", + "name": "Ramachandra Mohan", + "avatar_url": "https://avatars.githubusercontent.com/u/22360770?v=4", + "profile": "https://github.com/ramachandra-mohan", + "contributions": [ + "bug" + ] + }, + { + "login": "ravikiranj", + "name": "Ravikiran Janardhana", + "avatar_url": "https://avatars.githubusercontent.com/u/389491?v=4", + "profile": "http://ravikiranj.net/", + "contributions": [ + "bug" + ] + }, + { + "login": "rblasch", + "name": "Ronald Blaschke", + "avatar_url": "https://avatars.githubusercontent.com/u/52590?v=4", + "profile": "http://www.rblasch.org/", + "contributions": [ + "bug" + ] + }, + { + "login": "rdicroce", + "name": "Rich DiCroce", + "avatar_url": "https://avatars.githubusercontent.com/u/1458922?v=4", + "profile": "https://github.com/rdicroce", + "contributions": [ + "bug" + ] + }, + { + "login": "recdevs", + "name": "recdevs", + "avatar_url": "https://avatars.githubusercontent.com/u/63118273?v=4", + "profile": "https://github.com/recdevs", + "contributions": [ + "bug" + ] + }, + { + "login": "recrsn", + "name": "Amitosh Swain Mahapatra", + "avatar_url": "https://avatars.githubusercontent.com/u/16816719?v=4", + "profile": "https://amitosh.in/", + "contributions": [ + "bug" + ] + }, + { + "login": "RedaBenh", + "name": "Reda Benhemmouche", + "avatar_url": "https://avatars.githubusercontent.com/u/1609466?v=4", + "profile": "https://twitter.com/reda_benh", + "contributions": [ + "bug" + ] + }, + { + "login": "regrog", + "name": "Andrea", + "avatar_url": "https://avatars.githubusercontent.com/u/7606880?v=4", + "profile": "https://github.com/regrog", + "contributions": [ + "bug" + ] + }, + { + "login": "Reissner", + "name": "Ernst Reissner", + "avatar_url": "https://avatars.githubusercontent.com/u/5049888?v=4", + "profile": "https://github.com/Reissner", + "contributions": [ + "bug" + ] + }, + { + "login": "Rest0", + "name": "Julian Voronetsky", + "avatar_url": "https://avatars.githubusercontent.com/u/6252887?v=4", + "profile": "https://github.com/Rest0", + "contributions": [ + "bug" + ] + }, + { + "login": "rijkt", + "name": "rijkt", + "avatar_url": "https://avatars.githubusercontent.com/u/56129985?v=4", + "profile": "https://github.com/rijkt", + "contributions": [ + "bug" + ] + }, + { + "login": "rillig-tk", + "name": "rillig-tk", + "avatar_url": "https://avatars.githubusercontent.com/u/46376960?v=4", + "profile": "https://github.com/rillig-tk", + "contributions": [ + "bug" + ] + }, + { + "login": "RiotR1cket", + "name": "Riot R1cket", + "avatar_url": "https://avatars.githubusercontent.com/u/33143437?v=4", + "profile": "https://developer.riotgames.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "rishabhdeepsingh", + "name": "RishabhDeep Singh", + "avatar_url": "https://avatars.githubusercontent.com/u/28526643?v=4", + "profile": "https://github.com/rishabhdeepsingh", + "contributions": [ + "bug" + ] + }, + { + "login": "RobertHenry6bev", + "name": "Robert Henry", + "avatar_url": "https://avatars.githubusercontent.com/u/4371939?v=4", + "profile": "https://github.com/RobertHenry6bev", + "contributions": [ + "bug" + ] + }, + { + "login": "robertpainsi", + "name": "Robert Painsi", + "avatar_url": "https://avatars.githubusercontent.com/u/1794599?v=4", + "profile": "https://github.com/robertpainsi", + "contributions": [ + "bug" + ] + }, + { + "login": "robertwhitebit", + "name": "Robert Whitebit", + "avatar_url": "https://avatars.githubusercontent.com/u/11049034?v=4", + "profile": "https://github.com/robertwhitebit", + "contributions": [ + "bug" + ] + }, + { + "login": "Robin-Wils", + "name": "Robin Wils", + "avatar_url": "https://avatars.githubusercontent.com/u/5617025?v=4", + "profile": "https://www.robinwils.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "RochusOest", + "name": "RochusOest", + "avatar_url": "https://avatars.githubusercontent.com/u/65393072?v=4", + "profile": "https://github.com/RochusOest", + "contributions": [ + "bug" + ] + }, + { + "login": "RocketRider", + "name": "Michael Möbius", + "avatar_url": "https://avatars.githubusercontent.com/u/1881640?v=4", + "profile": "http://www.rrsoftware.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "rodolfonoviski", + "name": "Rodolfo Noviski", + "avatar_url": "https://avatars.githubusercontent.com/u/7316374?v=4", + "profile": "https://github.com/rodolfonoviski", + "contributions": [ + "bug" + ] + }, + { + "login": "roipoussiere", + "name": "Nathanaël", + "avatar_url": "https://avatars.githubusercontent.com/u/1665542?v=4", + "profile": "https://mastodon.tetaneutral.net/@roipoussiere", + "contributions": [ + "bug" + ] + }, + { + "login": "romge", + "name": "Georg Romstorfer", + "avatar_url": "https://avatars.githubusercontent.com/u/56720952?v=4", + "profile": "https://github.com/romge", + "contributions": [ + "bug" + ] + }, + { + "login": "RootG", + "name": "Görkem Mülayim", + "avatar_url": "https://avatars.githubusercontent.com/u/17679464?v=4", + "profile": "https://github.com/RootG", + "contributions": [ + "bug" + ] + }, + { + "login": "rpau", + "name": "Raquel Pau", + "avatar_url": "https://avatars.githubusercontent.com/u/1483433?v=4", + "profile": "http://www.walkmod.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "r-r-a-j", + "name": "r-r-a-j", + "avatar_url": "https://avatars.githubusercontent.com/u/33902071?v=4", + "profile": "https://github.com/r-r-a-j", + "contributions": [ + "bug" + ] + }, + { + "login": "rtfpessoa", + "name": "Rodrigo Fernandes", + "avatar_url": "https://avatars.githubusercontent.com/u/902384?v=4", + "profile": "https://rtfpessoa.xyz/", + "contributions": [ + "bug" + ] + }, + { + "login": "rwhogg", + "name": "Bob \"Wombat\" Hogg", + "avatar_url": "https://avatars.githubusercontent.com/u/2373856?v=4", + "profile": "https://github.com/rwhogg", + "contributions": [ + "bug" + ] + }, + { + "login": "rxmicro", + "name": "rxmicro", + "avatar_url": "https://avatars.githubusercontent.com/u/54791695?v=4", + "profile": "https://rxmicro.io/", + "contributions": [ + "bug" + ] + }, + { + "login": "s4ik4t", + "name": "Saikat Sengupta", + "avatar_url": "https://avatars.githubusercontent.com/u/41847480?v=4", + "profile": "https://saikat.hashnode.dev/", + "contributions": [ + "bug" + ] + }, + { + "login": "sabi0", + "name": "sabi0", + "avatar_url": "https://avatars.githubusercontent.com/u/11509875?v=4", + "profile": "https://github.com/sabi0", + "contributions": [ + "bug" + ] + }, + { + "login": "Saladoc", + "name": "Saladoc", + "avatar_url": "https://avatars.githubusercontent.com/u/36816545?v=4", + "profile": "https://github.com/Saladoc", + "contributions": [ + "bug" + ] + }, + { + "login": "SalesforceBobLightning", + "name": "Salesforce Bob Lightning", + "avatar_url": "https://avatars.githubusercontent.com/u/39457343?v=4", + "profile": "https://github.com/SalesforceBobLightning", + "contributions": [ + "bug" + ] + }, + { + "login": "SamCarlberg", + "name": "Sam Carlberg", + "avatar_url": "https://avatars.githubusercontent.com/u/6320992?v=4", + "profile": "https://github.com/SamCarlberg", + "contributions": [ + "bug" + ] + }, + { + "login": "scais", + "name": "scais", + "avatar_url": "https://avatars.githubusercontent.com/u/4539192?v=4", + "profile": "https://github.com/scais", + "contributions": [ + "bug" + ] + }, + { + "login": "schmittjoaopedro", + "name": "João Pedro Schmitt", + "avatar_url": "https://avatars.githubusercontent.com/u/2640413?v=4", + "profile": "http://joaoschmitt.wordpress.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "scottkennedy", + "name": "Scott Kennedy", + "avatar_url": "https://avatars.githubusercontent.com/u/881690?v=4", + "profile": "https://github.com/scottkennedy", + "contributions": [ + "bug" + ] + }, + { + "login": "SCWells72", + "name": "Scott Wells", + "avatar_url": "https://avatars.githubusercontent.com/u/7671043?v=4", + "profile": "https://github.com/SCWells72", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "Sebanisu", + "name": "Robert Russell", + "avatar_url": "https://avatars.githubusercontent.com/u/1035905?v=4", + "profile": "https://github.com/Sebanisu", + "contributions": [ + "bug" + ] + }, + { + "login": "sebbASF", + "name": "sebbASF", + "avatar_url": "https://avatars.githubusercontent.com/u/16689231?v=4", + "profile": "https://github.com/sebbASF", + "contributions": [ + "bug" + ] + }, + { + "login": "sgorbaty", + "name": "Sergey Gorbaty", + "avatar_url": "https://avatars.githubusercontent.com/u/407097?v=4", + "profile": "https://github.com/sgorbaty", + "contributions": [ + "bug" + ] + }, + { + "login": "shahamish150294", + "name": "Amish Shah", + "avatar_url": "https://avatars.githubusercontent.com/u/7998085?v=4", + "profile": "https://dev.to/shahamish", + "contributions": [ + "bug" + ] + }, + { + "login": "shilko2013", + "name": "shilko2013", + "avatar_url": "https://avatars.githubusercontent.com/u/33313482?v=4", + "profile": "https://github.com/shilko2013", + "contributions": [ + "bug" + ] + }, + { + "login": "simeonKondr", + "name": "simeonKondr", + "avatar_url": "https://avatars.githubusercontent.com/u/42644177?v=4", + "profile": "https://github.com/simeonKondr", + "contributions": [ + "bug" + ] + }, + { + "login": "skozlov", + "name": "Sergey Kozlov", + "avatar_url": "https://avatars.githubusercontent.com/u/3817455?v=4", + "profile": "http://skozlov.net/", + "contributions": [ + "bug" + ] + }, + { + "login": "snajberk", + "name": "snajberk", + "avatar_url": "https://avatars.githubusercontent.com/u/3585281?v=4", + "profile": "https://github.com/snajberk", + "contributions": [ + "bug" + ] + }, + { + "login": "sniperrifle2004", + "name": "sniperrifle2004", + "avatar_url": "https://avatars.githubusercontent.com/u/18223222?v=4", + "profile": "https://github.com/sniperrifle2004", + "contributions": [ + "bug" + ] + }, + { + "login": "snuyanzin", + "name": "snuyanzin", + "avatar_url": "https://avatars.githubusercontent.com/u/403174?v=4", + "profile": "https://github.com/snuyanzin", + "contributions": [ + "bug" + ] + }, + { + "login": "songxing10000", + "name": "xingsong", + "avatar_url": "https://avatars.githubusercontent.com/u/10040131?v=4", + "profile": "https://github.com/songxing10000", + "contributions": [ + "bug" + ] + }, + { + "login": "Springvar", + "name": "Ivar Andreas Bonsaksen", + "avatar_url": "https://avatars.githubusercontent.com/u/3775536?v=4", + "profile": "https://github.com/Springvar", + "contributions": [ + "bug" + ] + }, + { + "login": "sratz", + "name": "sratz", + "avatar_url": "https://avatars.githubusercontent.com/u/14908423?v=4", + "profile": "https://github.com/sratz", + "contributions": [ + "bug" + ] + }, + { + "login": "Srini1993", + "name": "Srinivasan Venkatachalam", + "avatar_url": "https://avatars.githubusercontent.com/u/24495100?v=4", + "profile": "https://github.com/Srini1993", + "contributions": [ + "bug" + ] + }, + { + "login": "sschuberth", + "name": "Sebastian Schuberth", + "avatar_url": "https://avatars.githubusercontent.com/u/349154?v=4", + "profile": "https://github.com/sschuberth", + "contributions": [ + "bug" + ] + }, + { + "login": "stefanbirkner", + "name": "Stefan Birkner", + "avatar_url": "https://avatars.githubusercontent.com/u/711349?v=4", + "profile": "https://www.stefan-birkner.de/", + "contributions": [ + "bug" + ] + }, + { + "login": "stefanoportelli", + "name": "Stexxe", + "avatar_url": "https://avatars.githubusercontent.com/u/26385026?v=4", + "profile": "https://github.com/stefanoportelli", + "contributions": [ + "bug" + ] + }, + { + "login": "stephengroat", + "name": "Stephen", + "avatar_url": "https://avatars.githubusercontent.com/u/1159138?v=4", + "profile": "http://www.stephengroat.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "stianlagstad", + "name": "Stian Lågstad", + "avatar_url": "https://avatars.githubusercontent.com/u/4340859?v=4", + "profile": "https://github.com/stianlagstad", + "contributions": [ + "bug" + ] + }, + { + "login": "stonio", + "name": "stonio", + "avatar_url": "https://avatars.githubusercontent.com/u/19952825?v=4", + "profile": "https://github.com/stonio", + "contributions": [ + "bug" + ] + }, + { + "login": "strkkk", + "name": "Andrei Paikin", + "avatar_url": "https://avatars.githubusercontent.com/u/8901354?v=4", + "profile": "https://github.com/strkkk", + "contributions": [ + "bug" + ] + }, + { + "login": "StuartClayton5", + "name": "StuartClayton5", + "avatar_url": "https://avatars.githubusercontent.com/u/3109872?v=4", + "profile": "https://github.com/StuartClayton5", + "contributions": [ + "bug" + ] + }, + { + "login": "Stwissel", + "name": "Stephan H. Wissel", + "avatar_url": "https://avatars.githubusercontent.com/u/542549?v=4", + "profile": "https://wissel.net/", + "contributions": [ + "bug" + ] + }, + { + "login": "sudharmohan", + "name": "sudharmohan", + "avatar_url": "https://avatars.githubusercontent.com/u/16752281?v=4", + "profile": "https://github.com/sudharmohan", + "contributions": [ + "bug" + ] + }, + { + "login": "SukJinKim", + "name": "ALiNew", + "avatar_url": "https://avatars.githubusercontent.com/u/42788336?v=4", + "profile": "https://alinew.tistory.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "SUPERCILEX", + "name": "Alex Saveau", + "avatar_url": "https://avatars.githubusercontent.com/u/9490724?v=4", + "profile": "https://alexsaveau.dev/", + "contributions": [ + "bug" + ] + }, + { + "login": "SupunArunoda", + "name": "Supun Arunoda", + "avatar_url": "https://avatars.githubusercontent.com/u/12041588?v=4", + "profile": "https://github.com/SupunArunoda", + "contributions": [ + "bug" + ] + }, + { + "login": "suren39", + "name": "Suren Abrahamyan", + "avatar_url": "https://avatars.githubusercontent.com/u/2401754?v=4", + "profile": "https://github.com/suren39", + "contributions": [ + "bug" + ] + }, + { + "login": "suruchidawar", + "name": "suruchidawar", + "avatar_url": "https://avatars.githubusercontent.com/u/30810931?v=4", + "profile": "https://github.com/suruchidawar", + "contributions": [ + "bug" + ] + }, + { + "login": "svenfinitiv", + "name": "svenfinitiv", + "avatar_url": "https://avatars.githubusercontent.com/u/5653724?v=4", + "profile": "https://github.com/svenfinitiv", + "contributions": [ + "bug" + ] + }, + { + "login": "SwatiBGupta1110", + "name": "SwatiBGupta1110", + "avatar_url": "https://avatars.githubusercontent.com/u/89257671?v=4", + "profile": "https://github.com/SwatiBGupta1110", + "contributions": [ + "bug" + ] + }, + { + "login": "swsms", + "name": "Artem", + "avatar_url": "https://avatars.githubusercontent.com/u/16266572?v=4", + "profile": "https://www.linkedin.com/in/artyom-burylov-8b62b4159/", + "contributions": [ + "bug" + ] + }, + { + "login": "SyedThoufich", + "name": "SyedThoufich", + "avatar_url": "https://avatars.githubusercontent.com/u/58038531?v=4", + "profile": "https://github.com/SyedThoufich", + "contributions": [ + "bug" + ] + }, + { + "login": "szyman23", + "name": "Piotr Szymański", + "avatar_url": "https://avatars.githubusercontent.com/u/4140681?v=4", + "profile": "https://github.com/szyman23", + "contributions": [ + "bug" + ] + }, + { + "login": "szymex", + "name": "Szymon Sasin", + "avatar_url": "https://avatars.githubusercontent.com/u/2721874?v=4", + "profile": "https://github.com/szymex", + "contributions": [ + "bug" + ] + }, + { + "login": "tashiscool", + "name": "tashiscool", + "avatar_url": "https://avatars.githubusercontent.com/u/1057457?v=4", + "profile": "https://github.com/tashiscool", + "contributions": [ + "bug" + ] + }, + { + "login": "T-chuangxin", + "name": "T-chuangxin", + "avatar_url": "https://avatars.githubusercontent.com/u/28993085?v=4", + "profile": "https://github.com/T-chuangxin", + "contributions": [ + "bug" + ] + }, + { + "login": "TedHusted", + "name": "Ted Husted", + "avatar_url": "https://avatars.githubusercontent.com/u/1641984?v=4", + "profile": "http://www.dreamops.org/", + "contributions": [ + "bug" + ] + }, + { + "login": "TehBakker", + "name": "TehBakker", + "avatar_url": "https://avatars.githubusercontent.com/u/7705294?v=4", + "profile": "https://github.com/TehBakker", + "contributions": [ + "bug" + ] + }, + { + "login": "test-git-hook", + "name": "test-git-hook", + "avatar_url": "https://avatars.githubusercontent.com/u/49142715?v=4", + "profile": "https://github.com/test-git-hook", + "contributions": [ + "bug" + ] + }, + { + "login": "thanosa", + "name": "thanosa", + "avatar_url": "https://avatars.githubusercontent.com/u/24596498?v=4", + "profile": "https://github.com/thanosa", + "contributions": [ + "bug" + ] + }, + { + "login": "TheBoegl", + "name": "Sebastian Bögl", + "avatar_url": "https://avatars.githubusercontent.com/u/1990469?v=4", + "profile": "https://github.com/TheBoegl", + "contributions": [ + "bug" + ] + }, + { + "login": "theneva", + "name": "Martin Lehmann", + "avatar_url": "https://avatars.githubusercontent.com/u/1404650?v=4", + "profile": "http://twitter.com/theneva", + "contributions": [ + "bug" + ] + }, + { + "login": "theodoor", + "name": "Theodoor", + "avatar_url": "https://avatars.githubusercontent.com/u/1332244?v=4", + "profile": "https://github.com/theodoor", + "contributions": [ + "bug" + ] + }, + { + "login": "TheRealHaui", + "name": "Michael Hausegger", + "avatar_url": "https://avatars.githubusercontent.com/u/6312834?v=4", + "profile": "https://github.com/TheRealHaui", + "contributions": [ + "bug" + ] + }, + { + "login": "thibaultmeyer", + "name": "Thibault Meyer", + "avatar_url": "https://avatars.githubusercontent.com/u/1005086?v=4", + "profile": "https://github.com/thibaultmeyer", + "contributions": [ + "bug" + ] + }, + { + "login": "Thihup", + "name": "Thiago Henrique Hüpner", + "avatar_url": "https://avatars.githubusercontent.com/u/13357965?v=4", + "profile": "https://github.com/Thihup", + "contributions": [ + "bug" + ] + }, + { + "login": "ThrawnCA", + "name": "ThrawnCA", + "avatar_url": "https://avatars.githubusercontent.com/u/3080440?v=4", + "profile": "https://github.com/ThrawnCA", + "contributions": [ + "bug" + ] + }, + { + "login": "tiandiyixian", + "name": "tiandiyixian", + "avatar_url": "https://avatars.githubusercontent.com/u/27055337?v=4", + "profile": "https://github.com/tiandiyixian", + "contributions": [ + "bug" + ] + }, + { + "login": "TimvdLippe", + "name": "Tim van der Lippe", + "avatar_url": "https://avatars.githubusercontent.com/u/5948271?v=4", + "profile": "https://github.com/TimvdLippe", + "contributions": [ + "bug" + ] + }, + { + "login": "tjoneslo", + "name": "Thomas Jones-Low", + "avatar_url": "https://avatars.githubusercontent.com/u/1969458?v=4", + "profile": "https://github.com/tjoneslo", + "contributions": [ + "bug" + ] + }, + { + "login": "tkleiber", + "name": "Torsten Kleiber", + "avatar_url": "https://avatars.githubusercontent.com/u/2185441?v=4", + "profile": "https://github.com/tkleiber", + "contributions": [ + "bug" + ] + }, + { + "login": "tobwoerk", + "name": "tobwoerk", + "avatar_url": "https://avatars.githubusercontent.com/u/11739442?v=4", + "profile": "https://github.com/tobwoerk", + "contributions": [ + "bug" + ] + }, + { + "login": "tomdaly", + "name": "Tom Daly", + "avatar_url": "https://avatars.githubusercontent.com/u/2606341?v=4", + "profile": "https://tdaly.co.uk/", + "contributions": [ + "bug" + ] + }, + { + "login": "TomerFi", + "name": "Tomer Figenblat", + "avatar_url": "https://avatars.githubusercontent.com/u/28388442?v=4", + "profile": "https://github.com/TomerFi", + "contributions": [ + "bug" + ] + }, + { + "login": "tophersmith", + "name": "Chris Smith", + "avatar_url": "https://avatars.githubusercontent.com/u/812876?v=4", + "profile": "https://github.com/tophersmith", + "contributions": [ + "bug" + ] + }, + { + "login": "touzoku", + "name": "Marat Vyshegorodtsev", + "avatar_url": "https://avatars.githubusercontent.com/u/1285662?v=4", + "profile": "https://github.com/touzoku", + "contributions": [ + "bug" + ] + }, + { + "login": "tprouvot", + "name": "tprouvot", + "avatar_url": "https://avatars.githubusercontent.com/u/35368290?v=4", + "profile": "https://github.com/tprouvot", + "contributions": [ + "bug" + ] + }, + { + "login": "TrackerSB", + "name": "TrackerSB", + "avatar_url": "https://avatars.githubusercontent.com/u/6358523?v=4", + "profile": "https://github.com/TrackerSB", + "contributions": [ + "bug" + ] + }, + { + "login": "trentchilders", + "name": "trentchilders", + "avatar_url": "https://avatars.githubusercontent.com/u/6664350?v=4", + "profile": "https://github.com/trentchilders", + "contributions": [ + "bug" + ] + }, + { + "login": "triandicAnt", + "name": "triandicAnt", + "avatar_url": "https://avatars.githubusercontent.com/u/2345902?v=4", + "profile": "https://github.com/triandicAnt", + "contributions": [ + "bug" + ] + }, + { + "login": "trishul14", + "name": "trishul14", + "avatar_url": "https://avatars.githubusercontent.com/u/24551131?v=4", + "profile": "https://github.com/trishul14", + "contributions": [ + "bug" + ] + }, + { + "login": "triskaj", + "name": "Jan Tříska", + "avatar_url": "https://avatars.githubusercontent.com/u/21357785?v=4", + "profile": "https://github.com/triskaj", + "contributions": [ + "bug" + ] + }, + { + "login": "tsmock", + "name": "Taylor Smock", + "avatar_url": "https://avatars.githubusercontent.com/u/45215054?v=4", + "profile": "https://github.com/tsmock", + "contributions": [ + "bug" + ] + }, + { + "login": "TWiStErRob", + "name": "Róbert Papp", + "avatar_url": "https://avatars.githubusercontent.com/u/2906988?v=4", + "profile": "https://github.com/TWiStErRob", + "contributions": [ + "bug" + ] + }, + { + "login": "uberbinge", + "name": "Waqas Ahmed", + "avatar_url": "https://avatars.githubusercontent.com/u/1692495?v=4", + "profile": "https://github.com/uberbinge", + "contributions": [ + "bug" + ] + }, + { + "login": "uhafner", + "name": "Ullrich Hafner", + "avatar_url": "https://avatars.githubusercontent.com/u/503338?v=4", + "profile": "http://www.cs.hm.edu/die_fakultaet/ansprechpartner/professoren/hafner/index.de.html", + "contributions": [ + "bug" + ] + }, + { + "login": "uncletall", + "name": "Peter Bruin", + "avatar_url": "https://avatars.githubusercontent.com/u/4156901?v=4", + "profile": "https://blog.bruin.sg/", + "contributions": [ + "bug" + ] + }, + { + "login": "vanlooverenkoen", + "name": "Koen Van Looveren", + "avatar_url": "https://avatars.githubusercontent.com/u/21172855?v=4", + "profile": "https://github.com/vanlooverenkoen", + "contributions": [ + "bug" + ] + }, + { + "login": "vanniktech", + "name": "Niklas Baudy", + "avatar_url": "https://avatars.githubusercontent.com/u/5759366?v=4", + "profile": "http://vanniktech.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "vbrandl", + "name": "Valentin Brandl", + "avatar_url": "https://avatars.githubusercontent.com/u/20639051?v=4", + "profile": "https://www.vbrandl.net/", + "contributions": [ + "bug" + ] + }, + { + "login": "vhuynh4thalesgroup", + "name": "Vincent HUYNH", + "avatar_url": "https://avatars.githubusercontent.com/u/50705525?v=4", + "profile": "https://github.com/vhuynh4thalesgroup", + "contributions": [ + "bug" + ] + }, + { + "login": "vickenty", + "name": "Vickenty Fesunov", + "avatar_url": "https://avatars.githubusercontent.com/u/914873?v=4", + "profile": "https://github.com/vickenty", + "contributions": [ + "bug" + ] + }, + { + "login": "victornoel", + "name": "Victor Noël", + "avatar_url": "https://avatars.githubusercontent.com/u/160975?v=4", + "profile": "http://www.irit.fr/~Victor.Noel/", + "contributions": [ + "bug" + ] + }, + { + "login": "Vishhwas", + "name": "Vishhwas", + "avatar_url": "https://avatars.githubusercontent.com/u/44495700?v=4", + "profile": "https://github.com/Vishhwas", + "contributions": [ + "bug" + ] + }, + { + "login": "vishva007", + "name": "avishvat", + "avatar_url": "https://avatars.githubusercontent.com/u/6756036?v=4", + "profile": "https://github.com/vishva007", + "contributions": [ + "bug" + ] + }, + { + "login": "vitarb", + "name": "Vitaly", + "avatar_url": "https://avatars.githubusercontent.com/u/1311694?v=4", + "profile": "https://github.com/vitarb", + "contributions": [ + "bug" + ] + }, + { + "login": "vmaurin", + "name": "Vincent Maurin", + "avatar_url": "https://avatars.githubusercontent.com/u/17569830?v=4", + "profile": "https://github.com/vmaurin", + "contributions": [ + "bug" + ] + }, + { + "login": "vojtapol", + "name": "Vojtech Polivka", + "avatar_url": "https://avatars.githubusercontent.com/u/7419355?v=4", + "profile": "https://github.com/vojtapol", + "contributions": [ + "bug" + ] + }, + { + "login": "vszholobov", + "name": "Vsevolod Zholobov", + "avatar_url": "https://avatars.githubusercontent.com/u/73242083?v=4", + "profile": "https://github.com/vszholobov", + "contributions": [ + "bug" + ] + }, + { + "login": "wata727", + "name": "Kazuma Watanabe", + "avatar_url": "https://avatars.githubusercontent.com/u/9624059?v=4", + "profile": "https://sil.hatenablog.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "Wchenghui", + "name": "Wchenghui", + "avatar_url": "https://avatars.githubusercontent.com/u/28353797?v=4", + "profile": "https://github.com/Wchenghui", + "contributions": [ + "bug" + ] + }, + { + "login": "willamette", + "name": "Chen Yang", + "avatar_url": "https://avatars.githubusercontent.com/u/1435016?v=4", + "profile": "https://github.com/willamette", + "contributions": [ + "bug" + ] + }, + { + "login": "wimdeblauwe", + "name": "Wim Deblauwe", + "avatar_url": "https://avatars.githubusercontent.com/u/1115823?v=4", + "profile": "https://github.com/wimdeblauwe", + "contributions": [ + "bug" + ] + }, + { + "login": "winder", + "name": "Will Winder", + "avatar_url": "https://avatars.githubusercontent.com/u/125509?v=4", + "profile": "http://www.willwinder.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "winhkey", + "name": "winhkey", + "avatar_url": "https://avatars.githubusercontent.com/u/4877808?v=4", + "profile": "https://github.com/winhkey", + "contributions": [ + "bug" + ] + }, + { + "login": "witherspore", + "name": "witherspore", + "avatar_url": "https://avatars.githubusercontent.com/u/813263?v=4", + "profile": "https://github.com/witherspore", + "contributions": [ + "bug" + ] + }, + { + "login": "wje600", + "name": "Wayne J. Earl", + "avatar_url": "https://avatars.githubusercontent.com/u/25891952?v=4", + "profile": "https://github.com/wje600", + "contributions": [ + "bug" + ] + }, + { + "login": "wjljack", + "name": "wjljack", + "avatar_url": "https://avatars.githubusercontent.com/u/1182478?v=4", + "profile": "https://github.com/wjljack", + "contributions": [ + "bug" + ] + }, + { + "login": "wkurniawan07", + "name": "Wilson Kurniawan", + "avatar_url": "https://avatars.githubusercontent.com/u/7261051?v=4", + "profile": "https://github.com/wkurniawan07", + "contributions": [ + "bug" + ] + }, + { + "login": "wolfs", + "name": "Stefan Wolf", + "avatar_url": "https://avatars.githubusercontent.com/u/423186?v=4", + "profile": "https://github.com/wolfs", + "contributions": [ + "bug" + ] + }, + { + "login": "wollamshram", + "name": "Andrew Green", + "avatar_url": "https://avatars.githubusercontent.com/u/18718483?v=4", + "profile": "https://github.com/wollamshram", + "contributions": [ + "bug" + ] + }, + { + "login": "woongsikchoi", + "name": "Woongsik Choi", + "avatar_url": "https://avatars.githubusercontent.com/u/3905257?v=4", + "profile": "https://github.com/woongsikchoi", + "contributions": [ + "bug" + ] + }, + { + "login": "wsdjeg", + "name": "Wang Shidong", + "avatar_url": "https://avatars.githubusercontent.com/u/13142418?v=4", + "profile": "https://wsdjeg.spacevim.org/", + "contributions": [ + "bug" + ] + }, + { + "login": "wuchiuwong", + "name": "wuchiuwong", + "avatar_url": "https://avatars.githubusercontent.com/u/15967553?v=4", + "profile": "https://github.com/wuchiuwong", + "contributions": [ + "bug" + ] + }, + { + "login": "xioayuge", + "name": "xioayuge", + "avatar_url": "https://avatars.githubusercontent.com/u/45328272?v=4", + "profile": "https://github.com/xioayuge", + "contributions": [ + "bug" + ] + }, + { + "login": "xmtsui", + "name": "tsui", + "avatar_url": "https://avatars.githubusercontent.com/u/1542690?v=4", + "profile": "https://github.com/xmtsui", + "contributions": [ + "bug" + ] + }, + { + "login": "xuanuy", + "name": "xuanuy", + "avatar_url": "https://avatars.githubusercontent.com/u/3894777?v=4", + "profile": "https://github.com/xuanuy", + "contributions": [ + "bug" + ] + }, + { + "login": "xyf0921", + "name": "xyf0921", + "avatar_url": "https://avatars.githubusercontent.com/u/17350974?v=4", + "profile": "https://github.com/xyf0921", + "contributions": [ + "bug" + ] + }, + { + "login": "xylo", + "name": "Stefan Endrullis", + "avatar_url": "https://avatars.githubusercontent.com/u/577869?v=4", + "profile": "https://github.com/xylo", + "contributions": [ + "bug" + ] + }, + { + "login": "yalechen-cyw3", + "name": "yalechen-cyw3", + "avatar_url": "https://avatars.githubusercontent.com/u/34886223?v=4", + "profile": "https://github.com/yalechen-cyw3", + "contributions": [ + "bug" + ] + }, + { + "login": "YaroslavTER", + "name": "YaroslavTER", + "avatar_url": "https://avatars.githubusercontent.com/u/13270181?v=4", + "profile": "https://github.com/YaroslavTER", + "contributions": [ + "bug" + ] + }, + { + "login": "yasuharu-sato", + "name": "yasuharu-sato", + "avatar_url": "https://avatars.githubusercontent.com/u/45546628?v=4", + "profile": "https://github.com/yasuharu-sato", + "contributions": [ + "bug" + ] + }, + { + "login": "yippie", + "name": "Kai Amundsen", + "avatar_url": "https://avatars.githubusercontent.com/u/2078328?v=4", + "profile": "https://github.com/yippie", + "contributions": [ + "bug" + ] + }, + { + "login": "ylexus", + "name": "Alexey Yudichev", + "avatar_url": "https://avatars.githubusercontent.com/u/6009806?v=4", + "profile": "https://github.com/ylexus", + "contributions": [ + "bug" + ] + }, + { + "login": "yuchen1013", + "name": "zt_soft", + "avatar_url": "https://avatars.githubusercontent.com/u/17316917?v=4", + "profile": "https://github.com/yuchen1013", + "contributions": [ + "bug" + ] + }, + { + "login": "yuridolzhenko", + "name": "Yuri Dolzhenko", + "avatar_url": "https://avatars.githubusercontent.com/u/1915205?v=4", + "profile": "https://github.com/yuridolzhenko", + "contributions": [ + "bug" + ] + }, + { + "login": "zenglian", + "name": "zenglian", + "avatar_url": "https://avatars.githubusercontent.com/u/5268434?v=4", + "profile": "https://github.com/zenglian", + "contributions": [ + "bug" + ] + }, + { + "login": "zhangxinngang", + "name": "zh3ng", + "avatar_url": "https://avatars.githubusercontent.com/u/6891146?v=4", + "profile": "https://github.com/zhangxinngang", + "contributions": [ + "bug" + ] + }, + { + "login": "zman0900", + "name": "Dan Ziemba", + "avatar_url": "https://avatars.githubusercontent.com/u/428589?v=4", + "profile": "https://github.com/zman0900", + "contributions": [ + "bug" + ] + }, + { + "login": "zolyfarkas", + "name": "Zoltan Farkas", + "avatar_url": "https://avatars.githubusercontent.com/u/144085?v=4", + "profile": "https://github.com/zolyfarkas", + "contributions": [ + "bug" + ] + }, + { + "login": "ztt79", + "name": "ztt79", + "avatar_url": "https://avatars.githubusercontent.com/u/48408552?v=4", + "profile": "https://github.com/ztt79", + "contributions": [ + "bug" + ] + }, + { + "login": "Zustin", + "name": "Zustin", + "avatar_url": "https://avatars.githubusercontent.com/u/87302257?v=4", + "profile": "https://github.com/Zustin", + "contributions": [ + "bug" + ] + }, + { + "login": "zyc-Iroha", + "name": "Iroha_", + "avatar_url": "https://avatars.githubusercontent.com/u/50617743?v=4", + "profile": "https://github.com/zyc-Iroha", + "contributions": [ + "bug" + ] + }, + { + "login": "zzzzfeng", + "name": "zzzzfeng", + "avatar_url": "https://avatars.githubusercontent.com/u/8851007?v=4", + "profile": "https://github.com/zzzzfeng", + "contributions": [ + "bug" + ] + }, + { + "login": "kevingnet", + "name": "Kevin Guerra", + "avatar_url": "https://avatars.githubusercontent.com/u/5151740?v=4", + "profile": "https://github.com/kevingnet", + "contributions": [ + "code" + ] + }, + { + "login": "smyachenkov", + "name": "Stanislav Myachenkov", + "avatar_url": "https://avatars.githubusercontent.com/u/10816424?v=4", + "profile": "https://smyachenkov.com/", + "contributions": [ + "code" + ] + }, + { + "login": "essobedo", + "name": "Nicolas Filotto", + "avatar_url": "https://avatars.githubusercontent.com/u/1618116?v=4", + "profile": "https://stackoverflow.com/users/1997376/nicolas-filotto", + "contributions": [ + "code" + ] + }, + { + "login": "lujiefsi", + "name": "lujiefsi", + "avatar_url": "https://avatars.githubusercontent.com/u/2918158?v=4", + "profile": "http://lujie.ac.cn/", + "contributions": [ + "code" + ] + }, + { + "login": "johnra2", + "name": "johnra2", + "avatar_url": "https://avatars.githubusercontent.com/u/90150885?v=4", + "profile": "https://github.com/johnra2", + "contributions": [ + "code" + ] + }, + { + "login": "duanyang25", + "name": "Yang", + "avatar_url": "https://avatars.githubusercontent.com/u/34642309?v=4", + "profile": "https://github.com/duanyang25", + "contributions": [ + "code" + ] + } + ], + "contributorsPerLine": 7, + "contributorsSortAlphabetically": true, + "skipCi": true +} diff --git a/.ci/README.md b/.ci/README.md index bbfa516d5c..87250d6ba7 100644 --- a/.ci/README.md +++ b/.ci/README.md @@ -8,7 +8,10 @@ It uses the common scripts from [build-tools](https://github.com/pmd/build-tools This files contains the following environment variables: * DANGER_GITHUB_API_TOKEN: Token for danger to add comments to PRs as . - The token needs the scope "public_repo". + The token needs the scope "public_repo". Note: The default GITHUB_TOKEN can't be used, because + danger runs in pull request builds from fork and the default GITHUB_TOKEN has read-only access there + and can't write comments. Therefore the personal access token of the bot account "pmd-test" is used. + pmd-test has no commit permissions, but can comment on any public repo, including pmd/pmd. * PMD_CI_CHUNK_TOKEN: Token for uploading reports to chunk.io The file is encrypted, so that the tokens are not automatically disabled when github detects them diff --git a/.ci/build.sh b/.ci/build.sh index 72704a2d4a..b7770d839f 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -9,14 +9,16 @@ SCRIPT_INCLUDES="log.bash utils.bash setup-secrets.bash openjdk.bash maven.bash source "$(dirname "$0")/inc/fetch_ci_scripts.bash" && fetch_ci_scripts function build() { - pmd_ci_log_group_start "Prepare Java 8+11, Bundler" - pmd_ci_openjdk_install_adoptopenjdk 11 + pmd_ci_log_group_start "Prepare Java 8+11+17, Bundler" + pmd_ci_openjdk_install_adoptium 11 pmd_ci_openjdk_setdefault 11 PMD_MAVEN_EXTRA_OPTS=() if [ "$(pmd_ci_utils_get_os)" = "linux" ]; then pmd_ci_log_info "Install openjdk8 for integration tests and pmd-regression-tests" - pmd_ci_openjdk_install_adoptopenjdk 8 - PMD_MAVEN_EXTRA_OPTS=(-Djava8.home="${HOME}/openjdk8") + pmd_ci_openjdk_install_adoptium 8 + pmd_ci_log_info "Install openjdk17 for integration tests and pmd-regression-tests" + pmd_ci_openjdk_install_adoptium 17 + PMD_MAVEN_EXTRA_OPTS=(-Djava8.home="${HOME}/openjdk8" -Djava17.home="${HOME}/openjdk17") fi pmd_ci_build_setup_bundler pmd_ci_log_group_end @@ -85,6 +87,23 @@ function build() { pmd_ci_log_group_end if pmd_ci_maven_isSnapshotBuild; then + if [ "${PMD_CI_MAVEN_PROJECT_VERSION}" != "7.0.0-SNAPSHOT" ]; then + pmd_ci_log_group_start "Executing PMD dogfood test with ${PMD_CI_MAVEN_PROJECT_VERSION}" + ./mvnw versions:set -DnewVersion="${PMD_CI_MAVEN_PROJECT_VERSION}-dogfood" -DgenerateBackupPoms=false + sed -i 's/[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}.*<\/version>\( *\)/'"${PMD_CI_MAVEN_PROJECT_VERSION}"'<\/version>\1/' pom.xml + ./mvnw verify --show-version --errors --batch-mode --no-transfer-progress "${PMD_MAVEN_EXTRA_OPTS[@]}" \ + -DskipTests \ + -Dmaven.javadoc.skip=true \ + -Dmaven.source.skip=true \ + -Dcheckstyle.skip=true + ./mvnw versions:set -DnewVersion="${PMD_CI_MAVEN_PROJECT_VERSION}" -DgenerateBackupPoms=false + git checkout -- pom.xml + pmd_ci_log_group_end + else + # current maven-pmd-plugin is not compatible with PMD 7 yet. + pmd_ci_log_info "Skipping PMD dogfood test with ${PMD_CI_MAVEN_PROJECT_VERSION}" + fi + pmd_ci_log_group_start "Executing build with sonar" # Note: Sonar also needs GITHUB_TOKEN (!) ./mvnw \ diff --git a/.ci/files/all-java.xml b/.ci/files/all-java.xml index b1005d2a2a..4934c62336 100644 --- a/.ci/files/all-java.xml +++ b/.ci/files/all-java.xml @@ -42,9 +42,11 @@ + + @@ -52,10 +54,6 @@ - - - - @@ -75,7 +73,6 @@ - @@ -106,6 +103,7 @@ + @@ -126,10 +124,10 @@ - - + + - + @@ -140,28 +138,27 @@ - + - + - - + - + - + - - + + @@ -174,9 +171,10 @@ - + + - + @@ -185,34 +183,33 @@ - + - + - - - + + + - + - + - + - - + - - + + @@ -225,48 +222,48 @@ - - - - - - - + + + + + + + + - + - - - - + + + - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + @@ -277,8 +274,8 @@ - - + + @@ -286,29 +283,21 @@ - - + + - - - - - + + - - - - - diff --git a/.ci/files/project-list.xml b/.ci/files/project-list.xml index 321d02e96d..1b43140068 100644 --- a/.ci/files/project-list.xml +++ b/.ci/files/project-list.xml @@ -8,10 +8,11 @@ xsi:noNamespaceSchemaLocation="projectlist_1_1_0.xsd"> checkstyle git https://github.com/checkstyle/checkstyle - checkstyle-8.10 + checkstyle-9.1 .*/target/test-classes/com/puppycrawl/tools/checkstyle/.* .*/target/generated-sources/.* + .*/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/javaparser/InputJavaParserNoFreezeOnDeeplyNestedLambdas.java @@ -30,7 +35,7 @@ mvn dependency:build-classpath -DincludeScope=test -Dmdep.outputFile=classpath.t spring-framework git https://github.com/spring-projects/spring-framework - v5.0.6.RELEASE + v5.3.13 .*/build/generated-sources/.* @@ -41,55 +46,95 @@ fi set -e -# Note: openjdk8 will be installed by "before_install.sh" -export JAVA_HOME=${HOME}/openjdk8 +# Make sure to use java11. This is already installed by build.sh +export JAVA_HOME=${HOME}/openjdk11 export PATH=$JAVA_HOME/bin:$PATH +## Patches # keep the tabs!! +# Patch 1: See https://github.com/spring-projects/spring-framework/commit/381b7d035a16d430b8783b7390c1677c9e7d1f68 +# and https://github.com/spring-projects/spring-framework/commit/9e1ed6c7718d38c4b9fe5f75921abad33264307c (cat < ann) { +- return determineRequiredStatus( ++ // Cast to (AnnotationAttributes) is required. Otherwise, the :spring-beans:compileGroovy ++ // task fails in the Gradle build. ++ return determineRequiredStatus((AnnotationAttributes) + ann.asMap(mergedAnnotation -> new AnnotationAttributes(mergedAnnotation.getType()))); } - repositories { -+ mavenCentral() - maven { url "https://repo.spring.io/libs-release" } - } +EOF +) | patch --strip=1 + +# Patch 2: Ignore compiler warnings +(cat < { + COMPILER_ARGS.addAll(commonCompilerArgs); + COMPILER_ARGS.addAll(Arrays.asList( + "-Xlint:varargs", "-Xlint:fallthrough", "-Xlint:rawtypes", "-Xlint:deprecation", +- "-Xlint:unchecked", "-Werror" ++ "-Xlint:unchecked"//, "-Werror" + )); + TEST_COMPILER_ARGS = new ArrayList<>(); + TEST_COMPILER_ARGS.addAll(commonCompilerArgs); +diff --git a/spring-beans/spring-beans.gradle b/spring-beans/spring-beans.gradle +index e3f6f73b76..48c4d9e3fb 100644 +--- a/spring-beans/spring-beans.gradle ++++ b/spring-beans/spring-beans.gradle +@@ -23,7 +23,7 @@ sourceSets { + } -@@ -314,3 +316,20 @@ + compileGroovy { +- options.compilerArgs += "-Werror" ++// options.compilerArgs += "-Werror" + } + + // This module also builds Kotlin code and the compileKotlin task naturally depends on +EOF +) | patch --strip=1 + +# Patch 3: Add task createSquishClasspath +(cat < classpath.txt +./gradlew --console=plain --build-cache --no-daemon --max-workers=4 build testClasses -x test -x javadoc -x api -x asciidoctor -x asciidoctorPdf +./gradlew --console=plain --build-cache --no-daemon --max-workers=4 createSquishClasspath -q > classpath.txt ]]> cat classpath.txt diff --git a/.ci/inc/pmd-doc.inc b/.ci/inc/pmd-doc.inc index aa0fa1e045..4a6d5acc14 100644 --- a/.ci/inc/pmd-doc.inc +++ b/.ci/inc/pmd-doc.inc @@ -55,9 +55,9 @@ function publish_release_documentation_github() { cd pmd.github.io || { echo "Directory 'pmd.github.io' doesn't exist"; exit 1; } git init git config user.name "PMD CI (pmd-bot)" - git config user.email "andreas.dangel+pmd-bot@adangel.org" + git config user.email "pmd-bot@users.noreply.github.com" git config core.sparsecheckout true - git remote add origin git@github.com:pmd/pmd.github.io.git + git remote add origin git@github.com-pmd.github.io:pmd/pmd.github.io.git echo "/latest/" > .git/info/sparse-checkout echo "/sitemap.xml" >> .git/info/sparse-checkout git pull --depth=1 origin master @@ -65,7 +65,7 @@ function publish_release_documentation_github() { rsync -ah --stats "../docs/pmd-doc-${PMD_CI_MAVEN_PROJECT_VERSION}/" "pmd-${PMD_CI_MAVEN_PROJECT_VERSION}/" git status pmd_ci_log_debug "Executing: git add pmd-${PMD_CI_MAVEN_PROJECT_VERSION}" - git add "pmd-${PMD_CI_MAVEN_PROJECT_VERSION}" + git add --sparse "pmd-${PMD_CI_MAVEN_PROJECT_VERSION}" pmd_ci_log_debug "Executing: git commit..." git commit -q -m "Added pmd-${PMD_CI_MAVEN_PROJECT_VERSION}" @@ -96,7 +96,7 @@ function publish_release_documentation_github() { function pmd_doc_publish_to_github_pages() { echo -e "\n\n" pmd_ci_log_info "Pushing the new site to github pages..." - git clone --branch gh-pages --depth 1 git@github.com:pmd/pmd.git pmd-gh-pages + git clone --branch gh-pages --depth 1 --origin origin https://github.com/pmd/pmd.git pmd-gh-pages # clear the files first rm -rf pmd-gh-pages/* # copy the new site @@ -104,14 +104,16 @@ function pmd_doc_publish_to_github_pages() { ( cd pmd-gh-pages || { echo "Directory 'pmd-gh-pages' doesn't exist"; exit 1; } git config user.name "PMD CI (pmd-bot)" - git config user.email "andreas.dangel+pmd-bot@adangel.org" + git config user.email "pmd-bot@users.noreply.github.com" + git config --local http.https://github.com/.extraheader "AUTHORIZATION: basic $(echo -n "x-access-token:${GITHUB_TOKEN}"|base64)" git add -A MSG="Update documentation ${PMD_CI_JOB_URL} ${PMD_CI_PUSH_COMMIT_COMPARE}" git commit -q -m "$MSG" - git push git@github.com:pmd/pmd.git HEAD:gh-pages + git push origin HEAD:gh-pages + git config --local --unset-all http.https://github.com/.extraheader pmd_ci_log_success "Successfully pushed site to https://pmd.github.io/pmd/" ) } diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 61353d4052..3fe7fa0e89 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -9,12 +9,10 @@ source "$(dirname "$0")/inc/fetch_ci_scripts.bash" && fetch_ci_scripts # The functions here require the following environment variables: # PMD_CI_BRANCH # -# DANGER_GITHUB_API_TOKEN +# GITHUB_TOKEN # PMD_CI_CHUNK_TOKEN function regression_tester_setup_ci() { - # note: building spring needs java8. This is setup already by build.sh - gpg --batch --yes --decrypt --passphrase="GnxdjywUEPveyCD1RLiTd7t8CImnefYr" \ --output .ci/files/public-env .ci/files/public-env.gpg # shellcheck disable=SC1091 @@ -69,19 +67,21 @@ function regression_tester_uploadBaseline() { function regression_tester_executeDanger() { pmd_ci_log_debug "${FUNCNAME[0]}" - # Create a corresponding remote branch locally - if ! git show-ref --verify --quiet "refs/heads/${PMD_CI_BRANCH}"; then - git fetch --no-tags --depth=1 origin "+refs/heads/${PMD_CI_BRANCH}:refs/remotes/origin/${PMD_CI_BRANCH}" - git branch "${PMD_CI_BRANCH}" "origin/${PMD_CI_BRANCH}" - pmd_ci_log_debug "Created local branch ${PMD_CI_BRANCH}" - fi - # Fetch more commits of the PR for danger and regression tester - git fetch --no-tags --depth=50 origin "+$(git rev-parse HEAD^2):" - # Fetch more commits from master branch for regression tester - if [[ "${PMD_CI_BRANCH}" != "master" ]]; then - git fetch --no-tags --depth=50 origin +master: - git branch master origin/master - fi + # git clone initially only fetched with depth 2. Danger and regression tester + # need more history, so we'll fetch more here + # and create local branches as well (${PMD_CI_BRANCH} and pr-fetch) + + pmd_ci_log_info "Fetching 25 commits for ${PMD_CI_BRANCH} and pull/${PMD_CI_PULL_REQUEST_NUMBER}/head" + git fetch --no-tags --depth=25 origin "${PMD_CI_BRANCH}:${PMD_CI_BRANCH}" "pull/${PMD_CI_PULL_REQUEST_NUMBER}/head:pr-fetch" + + # if the PR is older, base might have advanced more than 25 commits... fetch more, up to 150 + for i in $(seq 1 3); do + if [ -z "$( git merge-base "${PMD_CI_BRANCH}" "pr-fetch" )" ]; then + pmd_ci_log_info "No merge-base yet - fetching more commits... (try $i)" + git fetch --no-tags --deepen=50 origin "${PMD_CI_BRANCH}:" "pull/${PMD_CI_PULL_REQUEST_NUMBER}/head:pr-fetch" + fi + done + pmd_ci_log_info "Merge base is: $( git merge-base "${PMD_CI_BRANCH}" "pr-fetch" )" pmd_ci_log_info "Running danger on branch ${PMD_CI_BRANCH}" bundle exec danger --verbose diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a694e3ba2..87417153a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,9 +34,9 @@ jobs: ~/.cache ~/work/pmd/target/repositories vendor/bundle - key: v1-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + key: v2-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - v1-${{ runner.os }}- + v2-${{ runner.os }}- - name: Set up Ruby 2.7 uses: ruby/setup-ruby@v1 with: @@ -46,7 +46,7 @@ jobs: run: | echo "LANG=en_US.UTF-8" >> $GITHUB_ENV echo "MAVEN_OPTS=-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3 -DautoReleaseAfterClose=true -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV - echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/14/scripts" >> $GITHUB_ENV + echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/17/scripts" >> $GITHUB_ENV - name: Check Environment shell: bash run: | diff --git a/.github/workflows/git-repo-sync.yml b/.github/workflows/git-repo-sync.yml index 08514db95d..2219f2330a 100644 --- a/.github/workflows/git-repo-sync.yml +++ b/.github/workflows/git-repo-sync.yml @@ -22,9 +22,10 @@ jobs: shell: bash run: | echo "LANG=en_US.UTF-8" >> $GITHUB_ENV - echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/14/scripts" >> $GITHUB_ENV + echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/17/scripts" >> $GITHUB_ENV - name: Sync run: .ci/git-repo-sync.sh shell: bash env: PMD_CI_SECRET_PASSPHRASE: ${{ secrets.PMD_CI_SECRET_PASSPHRASE }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/troubleshooting.yml b/.github/workflows/troubleshooting.yml index 3b1f3b981c..d58e0f2aa1 100644 --- a/.github/workflows/troubleshooting.yml +++ b/.github/workflows/troubleshooting.yml @@ -21,9 +21,9 @@ jobs: ~/.cache ~/work/pmd/target/repositories vendor/bundle - key: v1-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + key: v2-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - v1-${{ runner.os }}- + v2-${{ runner.os }}- - name: Set up Ruby 2.7 uses: ruby/setup-ruby@v1 with: @@ -33,7 +33,7 @@ jobs: run: | echo "LANG=en_US.UTF-8" >> $GITHUB_ENV echo "MAVEN_OPTS=-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3 -DstagingProgressTimeoutMinutes=30" >> $GITHUB_ENV - echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/14/scripts" >> $GITHUB_ENV + echo "PMD_CI_SCRIPTS_URL=https://raw.githubusercontent.com/pmd/build-tools/17/scripts" >> $GITHUB_ENV - name: Check Environment shell: bash run: | @@ -48,7 +48,7 @@ jobs: mkdir -p .ci/inc && \ ( [ -e .ci/inc/$f ] || curl -sSL "${PMD_CI_SCRIPTS_URL}/inc/$f" > ".ci/inc/$f" ) && \ source .ci/inc/$f ; \ - pmd_ci_openjdk_install_adoptopenjdk 11 ; \ + pmd_ci_openjdk_install_adoptium 11 ; \ pmd_ci_openjdk_setdefault 11 shell: bash - name: Setup tmate session diff --git a/.gitignore b/.gitignore index 51a3ccab88..a4b24e4553 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,8 @@ pmd-core/dependency-reduced-pom.xml vendor .DS_Store +# node modules for https://allcontributors.org/docs/en/cli/installation +node_modules + # rule docs are generated docs/pages/pmd/rules diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index ffdc10e59f..598fb34156 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0172b4acc9..05419c8960 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,4 +57,20 @@ See [pmd-checkstyle-config.xml](https://github.com/pmd/build-tools/blob/master/s [the eclipse configuration files](https://github.com/pmd/build-tools/tree/master/eclipse) that can be imported into a fresh workspace. +## Add yourself as contributor +We use [All Contributors](https://allcontributors.org/en). + +To add yourself to the table of contributors, follow the +[bot usage instructions](https://allcontributors.org/docs/en/bot/usage) ;). + +Or use the CLI: + +1. Install the CLI: `npm i` (in PMD's top level directory) +2. Add yourself: `npx all-contributors add ` + +Where `username` is your GitHub username and `contribution` is a `,`-separated list +of contributions. See [Emoji Key](https://allcontributors.org/docs/en/emoji-key) for a list +of valid types. Common types are: "code", "doc", "bug", "blog", "talk", "test", "tutorial". + +See also [cli documentation](https://allcontributors.org/docs/en/cli/usage) diff --git a/Dangerfile b/Dangerfile index c410796144..d8f2e28dc3 100644 --- a/Dangerfile +++ b/Dangerfile @@ -12,7 +12,7 @@ def get_args(base_branch, autogen = TRUE, patch_config = './pmd/.ci/files/all-ja '--patch-branch', 'HEAD', '--patch-config', patch_config, '--mode', 'online', - autogen ? '--auto-gen-config' : '--filter-with-patch-config', + # autogen ? '--auto-gen-config' : '--filter-with-patch-config', '--keep-reports', '--error-recovery', '--baseline-download-url', 'https://pmd-code.org/pmd-regression-tester/', diff --git a/Gemfile b/Gemfile index 5f1df88228..5e0616a5b4 100644 --- a/Gemfile +++ b/Gemfile @@ -3,8 +3,8 @@ source 'https://rubygems.org/' # bleeding edge from git #gem 'pmdtester', :git => 'https://github.com/pmd/pmd-regression-tester.git', branch: 'master' -gem 'pmdtester', '~> 1' -gem 'danger', '~> 5.6', '>= 5.6' +gem 'pmdtester' +gem 'danger' # This group is only needed for rendering release notes (docs/render_release_notes.rb) # this happens during release (.ci/build.sh and do-release.sh) diff --git a/Gemfile.lock b/Gemfile.lock index b1ef397852..c6d7d32019 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.7.0) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) claide (1.0.3) claide-plugins (0.9.2) @@ -12,39 +12,60 @@ GEM concurrent-ruby (1.1.9) cork (0.3.0) colored2 (~> 3.1) - danger (5.16.1) + danger (8.4.0) claide (~> 1.0) claide-plugins (>= 0.9.2) colored2 (~> 3.1) cork (~> 0.1) - faraday (~> 0.9) - faraday-http-cache (~> 1.0) - git (~> 1.5) - kramdown (~> 1.5) + faraday (>= 0.9.0, < 2.0) + faraday-http-cache (~> 2.0) + git (~> 1.7) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.0) no_proxy_fix octokit (~> 4.7) - terminal-table (~> 1) + terminal-table (>= 1, < 4) differ (0.1.2) - et-orbi (1.2.4) + et-orbi (1.2.5) tzinfo - faraday (0.17.4) + faraday (1.8.0) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0.1) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.1) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) multipart-post (>= 1.2, < 3) - faraday-http-cache (1.3.1) - faraday (~> 0.8) - fugit (1.5.0) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-http-cache (2.2.0) + faraday (>= 0.8) + faraday-httpclient (1.0.1) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + fugit (1.5.2) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) - git (1.8.1) + git (1.9.1) rchardet (~> 1.8) - kramdown (1.17.0) - liquid (5.0.1) + kramdown (2.3.1) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (5.1.0) logger-colors (1.0.0) - mini_portile2 (2.5.3) + mini_portile2 (2.6.1) multipart-post (2.1.1) nap (1.1.0) no_proxy_fix (0.1.2) - nokogiri (1.11.7) - mini_portile2 (~> 2.5.0) + nokogiri (1.12.5) + mini_portile2 (~> 2.6.1) racc (~> 1.4) octokit (4.21.0) faraday (>= 0.9) @@ -61,27 +82,29 @@ GEM raabro (1.4.0) racc (1.5.2) rchardet (1.8.0) - rouge (3.26.0) - rufus-scheduler (3.7.0) + rexml (3.2.5) + rouge (3.26.1) + ruby2_keywords (0.0.5) + rufus-scheduler (3.8.0) fugit (~> 1.1, >= 1.1.6) safe_yaml (1.0.5) sawyer (0.8.2) addressable (>= 2.3.5) faraday (> 0.8, < 2.0) slop (4.9.1) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) tzinfo (2.0.4) concurrent-ruby (~> 1.0) - unicode-display_width (1.7.0) + unicode-display_width (2.1.0) PLATFORMS ruby DEPENDENCIES - danger (~> 5.6, >= 5.6) + danger liquid (>= 4.0.0) - pmdtester (~> 1) + pmdtester rouge (>= 1.7, < 4) safe_yaml (>= 1.0) diff --git a/README.md b/README.md index 7c2bde606a..fa68c3347b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PMD +# PMD - source code analyzer ![PMD Logo](https://raw.githubusercontent.com/pmd/pmd/pmd/7.0.x/docs/images/logo/pmd-logo-300px.png) @@ -9,18 +9,42 @@ [![Coverage Status](https://coveralls.io/repos/github/pmd/pmd/badge.svg)](https://coveralls.io/github/pmd/pmd) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a674ee8642ed44c6ba7633626ee95967)](https://www.codacy.com/app/pmd/pmd?utm_source=github.com&utm_medium=referral&utm_content=pmd/pmd&utm_campaign=Badge_Grade) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](code_of_conduct.md) - -## About +[![Documentation (latest)](https://img.shields.io/badge/docs-latest-green)](https://pmd.github.io/latest/) **PMD** is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, -unnecessary object creation, and so forth. It supports Java, JavaScript, Salesforce.com Apex and Visualforce, +unnecessary object creation, and so forth. It supports many languages. It can be extended with custom rules. +It uses JavaCC and Antlr to parse source files into abstract syntax trees (AST) and runs rules against them to find violations. +Rules can be written in Java or using a XPath query. + +It supports Java, JavaScript, Salesforce.com Apex and Visualforce, Modelica, PLSQL, Apache Velocity, XML, XSL, Scala. Additionally it includes **CPD**, the copy-paste-detector. CPD finds duplicated code in C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Lua, Matlab, Modelica, Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift, Visualforce and XML. -## Support +In the future we hope to add support for data/control flow analysis and automatic (quick) fixes where +it makes sense. + +## 🚀 Installation and Usage + +Download the latest binary zip from the [releases](https://github.com/pmd/pmd/releases/latest) +and extract it somewhere. + +Execute `bin/run.sh pmd` or `bin\pmd.bat`. + +See also [Getting Started](https://pmd.github.io/latest/pmd_userdocs_installation.html) + +**Demo:** + +This shows how PMD can detect for loops, that can be replaced by for-each loops. + +![Demo](docs/images/userdocs/pmd-demo.gif) + +There are plugins for Maven and Gradle as well as for various IDEs. +See [Tools / Integrations](https://pmd.github.io/latest/pmd_userdocs_tools.html) + +## ℹ️ How to get support? * How do I? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd) or on [discussions](https://github.com/pmd/pmd/discussions). @@ -31,7 +55,9 @@ Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift, * I have a quick question -- ask on our [Gitter chat](https://gitter.im/pmd/pmd). * Where's your documentation? -- -## Source +## 🤝 Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Our latest source of PMD can be found on [GitHub](https://github.com/pmd/pmd). Fork us! @@ -42,6 +68,17 @@ The rule designer is developed over at [pmd/pmd-designer](https://github.com/pmd Please see [its README](https://github.com/pmd/pmd-designer#contributing) for developer documentation. -## Website +## 💵 Financial Contributors -More information can be found on our [Website](https://pmd.github.io). +Become a financial contributor and help us sustain our community. [Contribute](https://opencollective.com/pmd/contribute) + +## ✨ Contributors + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. +Contributions of any kind welcome! + +See [credits](docs/pages/pmd/projectdocs/credits.md) for the complete list. + +## 📝 License + +[BSD Style](LICENSE) diff --git a/do-release.sh b/do-release.sh index f18884b38d..5cd900bf32 100755 --- a/do-release.sh +++ b/do-release.sh @@ -103,7 +103,7 @@ read -r STATS=$( echo "### Stats" echo "* $(git log pmd_releases/"${LAST_VERSION}"..HEAD --oneline --no-merges |wc -l) commits" -echo "* $(curl -s https://api.github.com/repos/pmd/pmd/milestones|jq ".[] | select(.title == \"$RELEASE_VERSION\") | .closed_issues") closed tickets & PRs" +echo "* $(curl -s "https://api.github.com/repos/pmd/pmd/milestones?state=all&direction=desc&per_page=5"|jq ".[] | select(.title == \"$RELEASE_VERSION\") | .closed_issues") closed tickets & PRs" echo "* Days since last release: $(( ( $(date +%s) - $(git log --max-count=1 --format="%at" pmd_releases/"${LAST_VERSION}") ) / 86400))" ) @@ -221,7 +221,7 @@ This is a {{ site.pmd.release_type }} release. EOF -git commit -a -m "Prepare next development version" +git commit -a -m "Prepare next development version [skip ci]" git push origin "${CURRENT_BRANCH}" ./mvnw -B release:clean echo @@ -245,8 +245,8 @@ tweet="${tweet// /%20}" tweet="${tweet//:/%3A}" tweet="${tweet//#/%23}" tweet="${tweet//\//%2F}" -tweet="${tweet//$'\r'//}" -tweet="${tweet//$'\n'//%0A}" +tweet="${tweet//$'\r'/}" +tweet="${tweet//$'\n'/%0A}" echo "* Tweet about this release on https://twitter.com/pmd_analyzer:" echo " " echo diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 36eb9740c2..467c8ae763 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,13 +1,13 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.3.7) + activesupport (6.0.4.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) zeitwerk (~> 2.2, >= 2.2.2) - addressable (2.7.0) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) coffee-script (2.4.1) coffee-script-source @@ -16,8 +16,8 @@ GEM colorator (1.1.0) commonmarker (0.17.13) ruby-enum (~> 0.5) - concurrent-ruby (1.1.8) - dnsruby (1.61.5) + concurrent-ruby (1.1.9) + dnsruby (1.61.7) simpleidn (~> 0.1) em-websocket (0.5.2) eventmachine (>= 0.12.9) @@ -26,20 +26,30 @@ GEM ffi (>= 1.15.0) eventmachine (1.2.7) execjs (2.8.1) - faraday (1.4.1) + faraday (1.8.0) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0.1) faraday-net_http (~> 1.0) faraday-net_http_persistent (~> 1.1) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) multipart-post (>= 1.2, < 3) ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) + faraday-httpclient (1.0.1) faraday-net_http (1.0.1) - faraday-net_http_persistent (1.1.0) - ffi (1.15.0) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + ffi (1.15.4) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (214) - github-pages-health-check (= 1.17.0) + github-pages (219) + github-pages-health-check (= 1.17.7) jekyll (= 3.9.0) jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) @@ -59,19 +69,19 @@ GEM jekyll-seo-tag (= 2.7.1) jekyll-sitemap (= 1.4.0) jekyll-swiss (= 1.0.0) - jekyll-theme-architect (= 0.1.1) - jekyll-theme-cayman (= 0.1.1) - jekyll-theme-dinky (= 0.1.1) - jekyll-theme-hacker (= 0.1.2) - jekyll-theme-leap-day (= 0.1.1) - jekyll-theme-merlot (= 0.1.1) - jekyll-theme-midnight (= 0.1.1) - jekyll-theme-minimal (= 0.1.1) - jekyll-theme-modernist (= 0.1.1) - jekyll-theme-primer (= 0.5.4) - jekyll-theme-slate (= 0.1.1) - jekyll-theme-tactile (= 0.1.1) - jekyll-theme-time-machine (= 0.1.1) + jekyll-theme-architect (= 0.2.0) + jekyll-theme-cayman (= 0.2.0) + jekyll-theme-dinky (= 0.2.0) + jekyll-theme-hacker (= 0.2.0) + jekyll-theme-leap-day (= 0.2.0) + jekyll-theme-merlot (= 0.2.0) + jekyll-theme-midnight (= 0.2.0) + jekyll-theme-minimal (= 0.2.0) + jekyll-theme-modernist (= 0.2.0) + jekyll-theme-primer (= 0.6.0) + jekyll-theme-slate (= 0.2.0) + jekyll-theme-tactile (= 0.2.0) + jekyll-theme-time-machine (= 0.2.0) jekyll-titles-from-headings (= 0.5.3) jemoji (= 0.12.0) kramdown (= 2.3.1) @@ -82,11 +92,11 @@ GEM nokogiri (>= 1.10.4, < 2.0) rouge (= 3.26.0) terminal-table (~> 1.4) - github-pages-health-check (1.17.0) + github-pages-health-check (1.17.7) addressable (~> 2.3) dnsruby (~> 1.60) octokit (~> 4.0) - public_suffix (>= 2.0.2, < 5.0) + public_suffix (>= 3.0, < 5.0) typhoeus (~> 1.3) html-pipeline (2.14.0) activesupport (>= 2) @@ -152,45 +162,45 @@ GEM jekyll-sitemap (1.4.0) jekyll (>= 3.7, < 5.0) jekyll-swiss (1.0.0) - jekyll-theme-architect (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-cayman (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-dinky (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-hacker (0.1.2) + jekyll-theme-architect (0.2.0) jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) - jekyll-theme-leap-day (0.1.1) - jekyll (~> 3.5) + jekyll-theme-cayman (0.2.0) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) - jekyll-theme-merlot (0.1.1) - jekyll (~> 3.5) + jekyll-theme-dinky (0.2.0) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) - jekyll-theme-midnight (0.1.1) - jekyll (~> 3.5) + jekyll-theme-hacker (0.2.0) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) - jekyll-theme-minimal (0.1.1) - jekyll (~> 3.5) + jekyll-theme-leap-day (0.2.0) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) - jekyll-theme-modernist (0.1.1) - jekyll (~> 3.5) + jekyll-theme-merlot (0.2.0) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) - jekyll-theme-primer (0.5.4) + jekyll-theme-midnight (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-minimal (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-modernist (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-primer (0.6.0) jekyll (> 3.5, < 5.0) jekyll-github-metadata (~> 2.9) jekyll-seo-tag (~> 2.0) - jekyll-theme-slate (0.1.1) - jekyll (~> 3.5) + jekyll-theme-slate (0.2.0) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) - jekyll-theme-tactile (0.1.1) - jekyll (~> 3.5) + jekyll-theme-tactile (0.2.0) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) - jekyll-theme-time-machine (0.1.1) - jekyll (~> 3.5) + jekyll-theme-time-machine (0.2.0) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) jekyll-titles-from-headings (0.5.3) jekyll (>= 3.3, < 5.0) @@ -205,19 +215,19 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) liquid (4.0.3) - listen (3.5.1) + listen (3.7.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) - mini_portile2 (2.5.1) + mini_portile2 (2.6.1) minima (2.5.1) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) minitest (5.14.4) multipart-post (2.1.1) - nokogiri (1.11.5) - mini_portile2 (~> 2.5.0) + nokogiri (1.12.5) + mini_portile2 (~> 2.6.1) racc (~> 1.4) octokit (4.21.0) faraday (>= 0.9) @@ -233,8 +243,8 @@ GEM rouge (3.26.0) ruby-enum (0.9.0) i18n - ruby2_keywords (0.0.4) - rubyzip (2.3.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) safe_yaml (1.0.5) sass (3.7.4) sass-listen (~> 4.0.0) @@ -255,8 +265,8 @@ GEM thread_safe (~> 0.1) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) + unf_ext (0.0.8) + unicode-display_width (1.8.0) zeitwerk (2.4.2) PLATFORMS diff --git a/docs/_config.yml b/docs/_config.yml index 7c4bbba608..6c8784c7f0 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -2,8 +2,8 @@ repository: pmd/pmd pmd: version: 7.0.0-SNAPSHOT - previous_version: 6.36.0 - date: ??-?????-2021 + previous_version: 6.41.0 + date: ??-?????-2022 release_type: major # release types: major, minor, bugfix diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml index c743d80699..0d00f0602c 100644 --- a/docs/_data/sidebars/pmd_sidebar.yml +++ b/docs/_data/sidebars/pmd_sidebar.yml @@ -364,6 +364,9 @@ entries: - title: PLSQL url: /pmd_languages_plsql.html output: web, pdf + - title: Visualforce + url: /pmd_languages_visualforce.html + output: web, pdf - title: Developer Documentation output: web, pdf folderitems: @@ -397,8 +400,14 @@ entries: - title: Major contributions output: web, pdf subfolderitems: - - title: Adding a new language - url: /pmd_devdocs_major_adding_new_language.html + - title: Rule Guidelines + url: /pmd_devdocs_major_rule_guidelines.html + output: web, pdf + - title: Adding a new language (JavaCC) + url: /pmd_devdocs_major_adding_new_language_javacc.html + output: web, pdf + - title: Adding a new language (Antlr) + url: /pmd_devdocs_major_adding_new_language_antlr.html output: web, pdf - title: Adding a new CPD language url: /pmd_devdocs_major_adding_new_cpd_language.html diff --git a/docs/_data/xpath_funs.yml b/docs/_data/xpath_funs.yml index b2a7beb5f5..830217ae71 100644 --- a/docs/_data/xpath_funs.yml +++ b/docs/_data/xpath_funs.yml @@ -10,6 +10,22 @@ aliases: - &needs_typenode "The context node must be a {% jdoc jast::TypeNode %}" langs: + - name: "Any language" + ns: "pmd" + funs: + - name: fileName + returnType: "xs:string" + shortDescription: "Returns the current filename" + description: "Returns the current simple filename without path but including the extension. + This can be used to write rules that check filename naming conventions. + +

This function is available since PMD 6.38.0.

" + notes: "The function can be called on any node." + examples: + - code: "//b[pmd:fileName() = 'Foo.xml']" + outcome: "Matches any `<b>` tags in files called `Foo.xml`." + + - name: "Java" ns: "pmd-java" funs: diff --git a/docs/css/customstyles.css b/docs/css/customstyles.css index 22d69a1b6a..d4ac3d27ee 100644 --- a/docs/css/customstyles.css +++ b/docs/css/customstyles.css @@ -1098,3 +1098,8 @@ h4.panel-title { padding-top: 0px; margin-top: 0px; } + +.post-content .all-contributors-list img { + width: 100px; + height: 100px; +} diff --git a/docs/images/userdocs/pmd-demo.gif b/docs/images/userdocs/pmd-demo.gif new file mode 100644 index 0000000000..35839c6629 Binary files /dev/null and b/docs/images/userdocs/pmd-demo.gif differ diff --git a/docs/index.md b/docs/index.md index c143ec45ca..134e96f9f3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -19,7 +19,7 @@ additional_js: -## Overview +## 💡 Overview @@ -45,18 +45,24 @@ things, PMD can be run: **CPD**, the **copy-paste detector**, is also distributed with PMD. You can also use it in a variety of ways, which are [documented here](pmd_userdocs_cpd.html). -## Download +## 💾 Download The latest release of PMD can be downloaded from our [Github releases page](https://github.com/pmd/pmd/releases/latest). The Logo is available from the [Logo Project Page](pmd_projectdocs_logo.html). -## Documentation +## 📖 Documentation The rest of this page exposes the contents of the documentation site thematically, which you can further scope down using the blue filter buttons. To navigate the site, you may also use the search bar in the top right, or the sidebar on the left. +## ✨ Contributors + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. +Contributions of any kind welcome! + +See [credits](pmd_projectdocs_credits.html) for the complete list.
diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index 65a0e56b07..fec555d75e 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -74,8 +74,15 @@ The default version is always ES6. #### New Rules +##### Apex + * The Apex rule {% rule "apex/design/UnusedMethod" %} finds unused methods in your code. +##### Java + +* {% rule "java/codestyle/UnnecessaryBoxing" %} reports boxing and unboxing +conversions that may be made implicit. + #### Changed Rules ##### Java @@ -89,32 +96,57 @@ The default version is always ES6. reading and understanding the expressions. * {% rule "java/bestpractices/LooseCoupling" %}: the rule has a new property to allow some types to be coupled to (`allowedTypes`). * {% rule "java/errorprone/EmptyCatchBlock" %}: `CloneNotSupportedException` and `InterruptedException` are not special-cased anymore. Rename the exception parameter to `ignored` to ignore them. +* {% rule "java/errorprone/DontImportSun" %}: `sun.misc.Signal` is not special-cased anymore. * {% rule "java/codestyle/UseDiamondOperator" %}: the property `java7Compatibility` is removed. The rule now handles Java 7 properly without a property. * {% rule "java/design/SingularField" %}: Properties `checkInnerClasses` and `disallowNotAssignment` are removed. The rule is now more precise and will check these cases properly. +* {% rule "java/design/UseUtilityClass" %}: The property `ignoredAnnotations` has been removed. + +#### Deprecated Rules + #### Removed Rules The following previously deprecated rules have been finally removed: -* AbstractNaming (java-codestyle) -* AvoidFinalLocalVariable (java-codestyle) -* AvoidPrefixingMethodParameters (java-codestyle) -* DataflowAnomalyAnalysis (java-errorprone) -* ForLoopsMustUseBraces (java-codestyle) -* IfElseStmtsMustUseBraces (java-codestyle) -* IfStmtsMustUseBraces (java-codestyle) +* AbstractNaming (java-codestyle) -> use {% rule "java/codestyle/ClassNamingConventions" %} +* AvoidFinalLocalVariable (java-codestyle) -> not replaced +* AvoidPrefixingMethodParameters (java-codestyle) -> use {% rule "java/codestyle/FormalParameterNamingConventions" %} +* AvoidUsingShortType (java-performance) -> not replaced +* BadComparison (java-errorprone) -> use {% rule "java/errorprone/ComparisonWithNaN" %} +* BooleanInstantiation (java-performance) -> use {% rule "java/codestyle/UnnecessaryBoxing" %} and {% rule "java/bestpractices/PrimitiveWrapperInstantiation" %} +* ByteInstantiation (java-performance) -> use {% rule "java/codestyle/UnnecessaryBoxing" %} and {% rule "java/bestpractices/PrimitiveWrapperInstantiation" %} +* CloneThrowsCloneNotSupportedException (java-errorprone) -> not replaced +* DataflowAnomalyAnalysis (java-errorprone) -> not replaced +* DefaultPackage (java-codestyle) -> use {% rule "java/codestyle/CommentDefaultAccessModifier" %} +* DoNotCallSystemExit (java-errorprone) -> use {% rule "java/errorprone/DoNotTerminateVM" %} +* ForLoopsMustUseBraces (java-codestyle) -> use {% rule "java/codestyle/ControlStatementBraces" %} +* IfElseStmtsMustUseBraces (java-codestyle) -> use {% rule "java/codestyle/ControlStatementBraces" %} +* IfStmtsMustUseBraces (java-codestyle) -> use {% rule "java/codestyle/ControlStatementBraces" %} +* IntegerInstantiation (java-performance) -> use {% rule "java/codestyle/UnnecessaryBoxing" %} and {% rule "java/bestpractices/PrimitiveWrapperInstantiation" %} +* InvalidSlf4jMessageFormat (java-errorprone) -> use {% rule "java/errorprone/InvalidLogMessageFormat" %} * LoggerIsNotStaticFinal (java-errorprone) -* MIsLeadingVariableName (java-codestyle) -* ModifiedCyclomaticComplexity (java-design) -* PositionLiteralsFirstInCaseInsensitiveComparisons (java-bestpractices) -* PositionLiteralsFirstInComparisons (java-bestpractices) -* StdCyclomaticComplexity (java-design) +* LongInstantiation (java-performance) -> use {% rule "java/codestyle/UnnecessaryBoxing" %} and {% rule "java/bestpractices/PrimitiveWrapperInstantiation" %} +* MIsLeadingVariableName (java-codestyle) -> use {% rule "java/codestyle/FieldNamingConventions" %} +* MissingBreakInSwitch (java-errorprone) -> use {% rule "java/errorprone/ImplicitSwitchFallThrough" %} +* ModifiedCyclomaticComplexity (java-design) -> use {% rule "java/design/CyclomaticComplexity" %} +* PositionLiteralsFirstInCaseInsensitiveComparisons (java-bestpractices) -> use {% rule "java/bestpractices/LiteralsFirstInComparisons" %} +* PositionLiteralsFirstInComparisons (java-bestpractices) -> use {% rule "java/bestpractices/LiteralsFirstInComparisons" %} +* ReturnEmptyArrayRatherThanNull (java-errorprone) -> use {% rule "java/errorprone/ReturnEmptyCollectionRatherThanNull" %} +* ShortInstantiation (java-performance) -> use {% rule "java/codestyle/UnnecessaryBoxing" %} and {% rule "java/bestpractices/PrimitiveWrapperInstantiation" %} +* SimplifyBooleanAssertion (java-design) -> use {% rule "java/bestpractices/SimplifiableTestAssertion" %} +* SimplifyStartsWith (java-performance) -> not replaced +* StdCyclomaticComplexity (java-design) -> use {% rule "java/design/CyclomaticComplexity" %} * SuspiciousConstantFieldName (java-codestyle) +* UnnecessaryWrapperObjectCreation (java-performance) -> use the new rule {% rule "java/codestyle/UnnecessaryBoxing" %} * UnsynchronizedStaticDateFormatter (java-multithreading) +* UseAssertEqualsInsteadOfAssertTrue (java-bestpractices) -> use {% rule "java/bestpractices/SimplifiableTestAssertion" %} +* UseAssertNullInsteadOfAssertEquals (java-bestpractices) -> use {% rule "java/bestpractices/SimplifiableTestAssertion" %} +* UseAssertSameInsteadOfAssertEquals (java-bestpractices) -> use {% rule "java/bestpractices/SimplifiableTestAssertion" %} +* UseAssertTrueInsteadOfAssertEquals (java-bestpractices) -> use {% rule "java/bestpractices/SimplifiableTestAssertion" %} * VariableNamingConventions (apex-codestyle) -* VariableNamingConventions (java-codestyle) -* WhileLoopsMustUseBraces (java-codestyle) +* VariableNamingConventions (java-codestyle) -> use {% rule "java/codestyle/FieldNamingConventions" %} and such +* WhileLoopsMustUseBraces (java-codestyle) -> use {% rule "java/codestyle/ControlStatementBraces" %} ### Fixed Issues @@ -165,6 +197,8 @@ The following previously deprecated rules have been finally removed: * [#3218](https://github.com/pmd/pmd/pull/3218): \[java] Generalize UnnecessaryCast to flag all unnecessary casts * [#3221](https://github.com/pmd/pmd/issues/3221): \[java] PrematureDeclaration false positive for unused variables * [#3238](https://github.com/pmd/pmd/issues/3238): \[java] Improve ExprContext, fix FNs of UnnecessaryCast +* java-design + * [#2536](https://github.com/pmd/pmd/issues/2536): \[java] ClassWithOnlyPrivateConstructorsShouldBeFinal can't detect inner class * java-errorprone * [#659](https://github.com/pmd/pmd/issues/659): \[java] MissingBreakInSwitch - last default case does not contain a break * [#1005](https://github.com/pmd/pmd/issues/1005): \[java] CloneMethodMustImplementCloneable triggers for interfaces @@ -175,6 +209,9 @@ The following previously deprecated rules have been finally removed: * [#2880](https://github.com/pmd/pmd/issues/2880): \[java] CompareObjectsWithEquals - false negative with type res * [#2894](https://github.com/pmd/pmd/issues/2894): \[java] Improve MissingBreakInSwitch * [#3071](https://github.com/pmd/pmd/issues/3071): \[java] BrokenNullCheck FP with PMD 6.30.0 + * [#3087](https://github.com/pmd/pmd/issues/3087): \[java] UnnecessaryBooleanAssertion overlaps with SimplifiableTestAssertion + * [#3100](https://github.com/pmd/pmd/issues/3100): \[java] UseCorrectExceptionLogging FP in 6.31.0 + * [#3173](https://github.com/pmd/pmd/issues/3173): \[java] UseProperClassLoader false positive * [#3351](https://github.com/pmd/pmd/issues/3351): \[java] ConstructorCallsOverridableMethod ignores abstract methods * java-multithreading * [#2537](https://github.com/pmd/pmd/issues/2537): \[java] DontCallThreadRun can't detect the case that call run() in `this.run()` @@ -225,7 +262,8 @@ The metrics framework has been made simpler and more general. * [#1658](https://github.com/pmd/pmd/pull/1658): \[core] Node support for Antlr-based languages - [Matías Fraga](https://github.com/matifraga) * [#1698](https://github.com/pmd/pmd/pull/1698): \[core] [swift] Antlr Base Parser adapter and Swift Implementation - [Lucas Soncini](https://github.com/lsoncini) * [#1774](https://github.com/pmd/pmd/pull/1774): \[core] Antlr visitor rules - [Lucas Soncini](https://github.com/lsoncini) -* [#1877](https://github.com/pmd/pmd/pull/1877): \[swift] Feature/swift rules - [Matias Fraga](https://github.com/matifraga) +* [#1877](https://github.com/pmd/pmd/pull/1877): \[swift] Feature/swift rules - [Matías Fraga](https://github.com/matifraga) +* [#1881](https://github.com/pmd/pmd/pull/1881): \[doc] Add ANTLR documentation - [Matías Fraga](https://github.com/matifraga) * [#1882](https://github.com/pmd/pmd/pull/1882): \[swift] UnavailableFunction Swift rule - [Tomás de Lucca](https://github.com/tomidelucca) * [#2830](https://github.com/pmd/pmd/pull/2830): \[apex] Apexlink POC - [Kevin Jones](https://github.com/nawforce) diff --git a/docs/pages/next_major_development.md b/docs/pages/next_major_development.md index b38e8e6714..49f52bb472 100644 --- a/docs/pages/next_major_development.md +++ b/docs/pages/next_major_development.md @@ -246,6 +246,103 @@ the breaking API changes will be performed in 7.0.0. an API is tagged as `@Deprecated` or not in the latest minor release. During the development of 7.0.0, we may decide to remove some APIs that were not tagged as deprecated, though we'll try to avoid it." %} +#### 6.41.0 + +##### Command Line Interface + +The command line options for PMD and CPD now use GNU-syle long options format. E.g. instead of `-rulesets` the +preferred usage is now `--rulesets`. Alternatively one can still use the short option `-R`. +Some options also have been renamed to a more consistent casing pattern at the same time +(`--fail-on-violation` instead of `-failOnViolation`). +The old single-dash options are still supported but are deprecated and will be removed with PMD 7. +This change makes the command line interface more consistent within PMD and also less surprising +compared to other cli tools. + +The changes in detail for PMD: + +|old option |new option| +|-------------------------------|----------| +| `-rulesets` | `--rulesets` (or `-R`) | +| `-uri` | `--uri` | +| `-dir` | `--dir` (or `-d`) | +| `-filelist` | `--file-list` | +| `-ignorelist` | `--ignore-list` | +| `-format` | `--format` (or `-f`) | +| `-debug` | `--debug` | +| `-verbose` | `--verbose` | +| `-help` | `--help` | +| `-encoding` | `--encoding` | +| `-threads` | `--threads` | +| `-benchmark` | `--benchmark` | +| `-stress` | `--stress` | +| `-shortnames` | `--short-names` | +| `-showsuppressed` | `--show-suppressed` | +| `-suppressmarker` | `--suppress-marker` | +| `-minimumpriority` | `--minimum-priority` | +| `-property` | `--property` | +| `-reportfile` | `--report-file` | +| `-force-language` | `--force-language` | +| `-auxclasspath` | `--aux-classpath` | +| `-failOnViolation` | `--fail-on-violation` | +| `--failOnViolation` | `--fail-on-violation` | +| `-norulesetcompatibility` | `--no-ruleset-compatibility` | +| `-cache` | `--cache` | +| `-no-cache` | `--no-cache` | + +The changes in detail for CPD: + +|old option |new option| +|-----------------------|----------| +| `--failOnViolation` | `--fail-on-violation` | +| `-failOnViolation` | `--fail-on-violation` | +| `--filelist` | `--file-list` | + +#### 6.40.0 + +##### Experimental APIs + +* The interface {% jdoc apex::lang.apex.ast.ASTCommentContainer %} has been added to the Apex AST. + It provides a way to check whether a node contains at least one comment. Currently this is only implemented for + {% jdoc apex::lang.apex.ast.ASTCatchBlockStatement %} and used by the rule + {% rule apex/errorprone/EmptyCatchBlock %}. + This information is also available via XPath attribute `@ContainsComment`. + +#### 6.39.0 + +No changes. + +#### 6.38.0 + +No changes. + +#### 6.37.0 + +##### PMD CLI + +* PMD has a new CLI option `-force-language`. With that a language can be forced to be used for all input files, + irrespective of filenames. When using this option, the automatic language selection by extension is disabled + and all files are tried to be parsed with the given language. Parsing errors are ignored and unparsable files + are skipped. + + This option allows to use the xml language for files, that don't use xml as extension. + See also the examples on [PMD CLI reference](pmd_userdocs_cli_reference.html#analyze-other-xml-formats). + +##### Experimental APIs + +* The AST types and APIs around Sealed Classes are not experimental anymore: + * {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceDeclaration#isSealed() %}, + {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceDeclaration#isNonSealed() %}, + {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceDeclaration#getPermittedSubclasses() %} + * {% jdoc java::lang.java.ast.ASTPermitsList %} + +##### Internal API + +Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. +You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. + +* The inner class {% jdoc !!core::cpd.TokenEntry.State %} is considered to be internal API. + It will probably be moved away with PMD 7. + #### 6.36.0 No changes. @@ -1324,9 +1421,36 @@ large projects, with many duplications, it was causing `OutOfMemoryError`s (see is deprecated in favour of {% rule "java/bestpractices/UnusedAssignment" %} (`java-bestpractices`), which was introduced in PMD 6.26.0. -* The java rule {% rule "java/codestyle/DefaultPackage" %} has been deprecated in favor of +* The java rule `DefaultPackage` (java-codestyle) has been deprecated in favor of {% rule "java/codestyle/CommentDefaultAccessModifier" %}. -* The Java rule {% rule "java/errorprone/CloneThrowsCloneNotSupportedException" %} has been deprecated without +* The Java rule `CloneThrowsCloneNotSupportedException` (java-errorprone) has been deprecated without replacement. +* The following Java rules are deprecated and removed from the quickstart ruleset, + as the new rule {% rule java/bestpractices/SimplifiableTestAssertion %} merges + their functionality: + * `UseAssertEqualsInsteadOfAssertTrue` (java-bestpractices) + * `UseAssertNullInsteadOfAssertTrue` (java-bestpractices) + * `UseAssertSameInsteadOfAssertTrue` (java-bestpractices) + * `UseAssertTrueInsteadOfAssertEquals` (java-bestpractices) + * `SimplifyBooleanAssertion` (java-design) + +* The Java rule `ReturnEmptyArrayRatherThanNull` (java-errorprone) is deprecated and removed from + the quickstart ruleset, as the new rule {% rule java/errorprone/ReturnEmptyCollectionRatherThanNull %} + supersedes it. + +* The following Java rules are deprecated and removed from the quickstart ruleset, + as the new rule {% rule java/bestpractices/PrimitiveWrapperInstantiation %} merges + their functionality: + * java/performance/BooleanInstantiation + * java/performance/ByteInstantiation + * java/performance/IntegerInstantiation + * java/performance/LongInstantiation + * java/performance/ShortInstantiation + +* The Java rule java/performance/UnnecessaryWrapperObjectCreation is deprecated + with no planned replacement before PMD 7. In it's current state, the rule is not useful + as it finds only contrived cases of creating a primitive wrapper and unboxing it explicitly + in the same expression. In PMD 7 this and more cases will be covered by a + new rule `UnnecessaryBoxing`. diff --git a/docs/pages/pmd/devdocs/major_contributions/adding_a_new_antlr_based_language.md b/docs/pages/pmd/devdocs/major_contributions/adding_a_new_antlr_based_language.md new file mode 100644 index 0000000000..7f156054d7 --- /dev/null +++ b/docs/pages/pmd/devdocs/major_contributions/adding_a_new_antlr_based_language.md @@ -0,0 +1,156 @@ +--- +title: Adding PMD support for a new ANTLR grammar based language +short_title: Adding a new language with ANTLR +tags: [devdocs, extending] +summary: "How to add a new language to PMD using ANTLR grammar." +last_updated: July 21, 2019 +sidebar: pmd_sidebar +permalink: pmd_devdocs_major_adding_new_language_antlr.html +folder: pmd/devdocs + +# needs to be changed to branch master instead of pmd/7.0.x +# https://github.com/pmd/pmd/blob/pmd/7.0.x -> https://github.com/pmd/pmd/blob/master +--- + + +## 1. Start with a new sub-module. +* See pmd-swift for examples. + +## 2. Implement an AST parser for your language +* ANTLR will generate the parser for you based on the grammar file. The grammar file needs to be placed in the + folder `src/main/antlr4` in the appropriate sub package `ast` of the language. E.g. for swift, the grammar + file is [Swift.g4](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/ast/Swift.g4) + and is placed in the package `net.sourceforge.pmd.lang.swift.ast`. + +## 3. Create AST node classes +* The individual AST nodes are generated, but you need to define the common interface for them. +* You need a need to define the supertype interface for all nodes of the language. For that, we provide + [`AntlrNode`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/AntlrNode.java). +* See [`SwiftNode`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/SwiftNode.java) + as an example. +* Additionally, you need several base classes: + * a language specific inner node - these nodes represent the production rules from the grammar. + In Antlr, they are called "ParserRuleContext". We call them "InnerNode". Use the + base class from pmd-core + [`BaseAntlrInnerNode`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/BaseAntlrInnerNode.java) + . And example is [`SwiftInnerNode`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/SwiftInnerNode.java). + * a language specific root node - this provides the root of the AST and our parser will return + subtypes of this node. The root node itself is a "InnerNode". + See [`SwiftRootNode`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/SwiftRootNode.java). + * a language specific terminal node. + See [`SwiftTerminalNode`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/SwiftTerminalNode.java). + * a language specific error node. + See [`SwiftErrorNode`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/SwiftErrorNode.java). +* In order for the generated code to match and use our custom classes, we have a common ant script, that fiddles with + the generated code. The ant script is [`antlr4-wrapper.xml`](https://github.com/pmd/pmd/blob/pmd/7.0.x/antlr4-wrapper.xml) and + does not need to be adjusted - it has plenty of parameters to set. The ant script is added in the + language module's `pom.xml` where the parameters are set (e.g. name of root name class). Have a look at + Swift's example: [`pmd-swift/pom.xml`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/pom.xml). +* You can add additional methods in your "InnerNode" (e.g. `SwiftInnerNode`) that are available on all nodes. + But on most cases you won't need to do anything. + +## 4. Generate your parser +* Make sure, you have the property `true` in your `pom.xml` file. +* This is just a matter of building the language module. ANTLR is called via ant, and this step is added + to the phase `generate-sources`. So you can just call e.g. `./mvnw generate-source -pl pmd-swift` to + have the parser generated. +* The generated code will be placed under `target/generated-sources/antlr4` and will not be committed to + source control. +* You should review the [swift pom](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/pom.xml). + +## 5. Create a TokenManager +* This is needed to support CPD (copy paste detection) +* We provide a default implementation using [`AntlrTokenManager`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/AntlrTokenizer.java). +* You must create your own "AntlrTokenizer" such as we do with + [`SwiftTokenizer`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/cpd/SwiftTokenizer.java). +* If you wish to filter specific tokens (e.g. comments to support CPD suppression via "CPD-OFF" and "CPD-ON") + you can create your own implementation of + [`AntlrTokenFilter`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrTokenFilter.java). + You'll need to override then the protected method `getTokenFilter(AntlrTokenManager)` + and return your custom filter. See the tokenizer for C# as an exmaple: + [`CsTokenizer`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java). + + If you don't need a custom token filter, you don't need to override the method. It returns the default + `AntlrTokenFilter` which doesn't filter anything. + +## 6. Create a PMD parser “adapter” +* Create your own parser, that adapts the ANLTR interface to PMD's parser interface. +* We provide a [`AntlrBaseParser`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/AntlrBaseParser.java) + implementation that you need to extend to create your own adapter as we do with + [`PmdSwiftParser`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/PmdSwiftParser.java). + +## 7. Create a rule violation factory +* This is an optional step. Most like, the default implementation will do what you need. + The default implementation is [`DefaultRuleViolationFactory`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/impl/DefaultRuleViolationFactory.java). +* The purpose of a rule violation factory is to create a rule violation instance for your handler (spoiler). + In case you want to provide additional data in your rule violation, you can create a custom one. However, + adding additional date here is discouraged, as you would need a custom renderer to actually use this + additional data. Such extensions are not language agnostic. + +## 8. Create a version handler +* Now you need to create your version handler, as we did with [`SwiftHandler`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java). +* This class is sort of a gateway between PMD and all parsing logic specific to your language. It has 2 purposes: + * `getRuleViolationFactory` method returns an instance of your rule violation factory *(see step #7)*. + By default, this returns the default rule violation factory. + * `getParser` returns an instance of your parser adapter *(see step #6)*. + That's the only method, that needs to be implemented here. + +## 9. Create a parser visitor adapter +* A parser visitor adapter is not needed anymore with PMD 7. The visitor interface now provides a default + implementation. +* The visitor for ANTLR based AST is generated along the parser from the ANTLR grammar file. The + base interface for a visitor is [`AstVisitor`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AstVisitor.java). +* The generated visitor class for Swift is called `SwiftVisitor`. +* In order to help use this visitor later on, a base visitor class should be created. + See [`SwiftVisitorBase`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/SwiftVisitorBase.java) + as an example. + +## 10. Create a rule chain visitor +* This step is not needed anymore. For using rule chain, there is no additional adjustment necessary anymore + in the languages. +* This feature has been merged into AbstractRule via the overridable method + {% jdoc !!core::lang.rule.AbstractRule#buildTargetSelector() %}. Individual rules can make use of this optimization + by overriding this method and return an appropriate RuleTargetSelector. + +## 11. Make PMD recognize your language +* Create your own subclass of `net.sourceforge.pmd.lang.BaseLanguageModule`, see Swift as an example: + [`SwiftLanguageModule`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java). +* Add your default version with `addDefaultVersion` in your language module's constructor. +* Add for each additional version of your language a call to `addVersion` as well. +* Create the service registration via the text file `src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language`. Add your fully qualified class name as a single line into it. + +## 12. Create an abstract rule class for the language +* You need to create your own `AbstractRule` in order to interface your language with PMD's generic rule + execution. +* See [`AbstractSwiftRule`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/AbstractSwiftRule.java) as an example. +* While the rule basically just extends + [`AntlrBaseRule`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/antlr4/AntlrBaseRule.java) without adding anything, every language should have its own base class for rule. + This helps to organize the code. +* All other rules for your language should extend this class. The purpose of this class is to provide a visitor + via the method `buildVisitor()` for analyzing the AST. The provided visitor only implements the visit methods + for specific AST nodes. The other node types use the default behavior and you don't need to care about them. + +## 13. Create rules +* Creating rules is already pretty well documented in PMD - and it’s no different for a new language, except you + may have different AST nodes. +* PMD supports 2 types of rules, through visitors or XPath. +* To add a visitor rule: + * You need to extend the abstract rule you created on the previous step, you can use the swift + rule [UnavailableFunctionRule](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/bestpractices/UnavailableFunctionRule.java) + as an example. Note, that all rule classes should be suffixed with `Rule` and should be placed + in a package the corresponds to their category. +* To add an XPath rule you can follow our guide [Writing XPath Rules](pmd_userdocs_extending_writing_xpath_rules.html). + +## 14. Test the rules +* See UnavailableFunctionRuleTest for example. Each rule has it's own test class. +* You have to create the category rule set for your language *(see pmd-swift/src/main/resources/bestpractices.xml for example)* +* When executing the test class + * this triggers the unit test to read the corresponding XML file with the rule test data + *(see `UnavailableFunctionRule.xml` for example)* + * This test XML file contains sample pieces of code which should trigger a specified number of + violations of this rule. The unit test will execute the rule on this piece of code, and verify + that the number of violations matches. +* To verify the validity of all the created rulesets, create a subclass of `AbstractRuleSetFactoryTest` (*see `RuleSetFactoryTest` in pmd-swift for example)*. + This will load all rulesets and verify, that all required attributes are provided. + + *Note:* You'll need to add your ruleset to `categories.properties`, so that it can be found. diff --git a/docs/pages/pmd/devdocs/major_contributions/adding_new_language.md b/docs/pages/pmd/devdocs/major_contributions/adding_a_new_javacc_based_language.md similarity index 96% rename from docs/pages/pmd/devdocs/major_contributions/adding_new_language.md rename to docs/pages/pmd/devdocs/major_contributions/adding_a_new_javacc_based_language.md index 1ec23a6dd9..c3b470247c 100644 --- a/docs/pages/pmd/devdocs/major_contributions/adding_new_language.md +++ b/docs/pages/pmd/devdocs/major_contributions/adding_a_new_javacc_based_language.md @@ -1,11 +1,11 @@ --- -title: Adding PMD support for a new language -short_title: Adding a new language +title: Adding PMD support for a new JAVACC grammar based language +short_title: Adding a new language with JAVACC tags: [devdocs, extending] -summary: "How to add a new language to PMD." +summary: "How to add a new language to PMD using JAVACC grammar." last_updated: October 5, 2019 sidebar: pmd_sidebar -permalink: pmd_devdocs_major_adding_new_language.html +permalink: pmd_devdocs_major_adding_new_language_javacc.html folder: pmd/devdocs --- @@ -47,10 +47,9 @@ folder: pmd/devdocs ## 8. Create a version handler * Extend `AbstractLanguageVersionHandler` *(see VmHandler for example)* -* This class is sort of a gateway between PMD and all parsing logic specific to your language. It has 3 purposes: +* This class is sort of a gateway between PMD and all parsing logic specific to your language. It has 2 purposes: * `getRuleViolationFactory` method returns an instance of your rule violation factory *(see step #7)* * `getParser` returns an instance of your parser adapter *(see step #6)* - * `getDumpFacade` returns a `VisitorStarter` that allows to dump a text representation of the AST into a writer *(likely for debugging purposes)* ## 9. Create a parser visitor adapter * If you use JJT to generate your parser, it should also generate an interface for a parser visitor *(see VmParserVisitor for example)* diff --git a/docs/pages/pmd/devdocs/major_contributions/rule_guidelines.md b/docs/pages/pmd/devdocs/major_contributions/rule_guidelines.md new file mode 100644 index 0000000000..2b252ba996 --- /dev/null +++ b/docs/pages/pmd/devdocs/major_contributions/rule_guidelines.md @@ -0,0 +1,77 @@ +--- +title: Guidelines for standard rules +short_title: Rule guidelines +tags: [devdocs, extending] +summary: "Guidelines for rules that are included in the standard distribution" +last_updated: August, 2021 +sidebar: pmd_sidebar +permalink: pmd_devdocs_major_rule_guidelines.html +--- + +{% include note.html content=" +These guidelines are new and most rules don't follow these guidelines yet. +The goal is, that eventually all rules are updated. +" %} + +## Why do we need these guidelines? + +* To prevent low quality contributions +* To reduce time reviewing rules + +They just apply to rules included in the standard distribution. + +## Requirements for standard rules + +To be included in stock PMD, a rule needs + +* Broad applicability. It may be specific to a framework, but then, this framework should be widely used +* Solid documentation. See below +* If it's a performance rule: solid benchmarks. No micro-optimization rules +* No overlap with other rules + +## Dos/Don'ts (rule rules) + +* Rule naming + * **Don't** put the implementation of the rule in the name, because it will be awkward + if the scope of the rule changes + * Eg. *SwitchStmtShouldHaveDefault* -> since enums are a thing they don't necessarily + need to have a default anymore, they should be exhaustive. So the rule name lies now... + * Eg. *MissingBreakInSwitch* -> it's obvious that this is supposed to find fall-through + switches. Counting breaks is not a clever way to do it, but since it's in the name + we can't change it without renaming the rule. + * **Do** use rule names that name the underlying problem that violations exhibit + * Eg. instead of *SwitchStmtShouldHaveDefault*, use *NonExhaustiveSwitchStatement* -> this + is the problem, the description of the rule will clarify why it is a problem and how + to fix it (add a default, or add branches, or something else in the future) + * Eg. instead of *MissingBreakInSwitch*, use *SwitchCaseFallsThrough* + * **Don't** create several rules for instances of the same problem + * *EmptyIfStmt* and *EmptyWhileStmt* are actually the same problem, namely, + that there's useless syntax in the tree. + * **Don't** limit the rule name to strictly what the rule can do today + * Eg. *UnusedPrivateField* is a bad name. The problem is that there is an unused field, + not that it is private as well. If we had the ability to find unused package-private + fields, we would report them too. So if one day we get that ability, + using a name like *UnusedField* would allow us to keep the name. +* Rule messages + * **Do** write rule messages that neutrally point out a problem or construct that should + be reviewed ("Unnecessary parentheses") + * **Don't** write rule messages that give an order ("Avoid unnecessary parentheses") + especially without explaining why, like here + * **Don't** write rule messages that are tautological ("Unnecessary parentheses should be removed"). + The answer to this would be an annoyed "yes I know, so what?". +* **Do** use Markdown in rule descriptions and break lines at a reasonable 80 chars +* **Do** thoroughly comment rule examples. It must be obvious where to look +* **Do** comment your xpath expressions too + +## Rule description template + +* What the rule reports (1 summary line) +* Why the rule exists and where it might be useful (including, since which language version, etc) +* Blank line +* Explain all assumptions that the rule makes and keywords used in the previous paragraph. + ("overridden methods are ignored", "for the purposes of this rule, a 'visible' field is + non-private"). +* Describe known limitations if any +* Blank line +* For each property, explain how it modifies the assumptions and why you would want to use it. + **If you can't explain why it's there then it shouldn’t be there!** diff --git a/docs/pages/pmd/languages/visualforce.md b/docs/pages/pmd/languages/visualforce.md new file mode 100644 index 0000000000..cc48cf3f79 --- /dev/null +++ b/docs/pages/pmd/languages/visualforce.md @@ -0,0 +1,46 @@ +--- +title: Visualforce Support +permalink: pmd_languages_visualforce.html +author: Andreas Dangel +last_updated: October 2021 +--- + +## Type resolution + +Since PMD 6.30.0 support for type resolution has been added. + +The Visualforce AST now can resolve the data type of Visualforce expressions that reference +Apex Controller properties and Custom Object fields. This feature improves the precision of existing rules, +like {% rule vf/security/VfUnescapeEl %}. + +This can be configured using two environment variables: + +* `PMD_VF_APEXDIRECTORIES`: Comma separated list of directories for Apex classes. Absolute or relative + to the Visualforce directory. Default is `../classes`. Specifying an empty string will disable data type + resolution for Apex Controller properties. + +* `PMD_VF_OBJECTSDIRECTORIES`: Comma separated list of directories for Custom Objects. Absolute or relative + to the Visualforce directory. Default is `../objects`. Specifying an empty string will disable data type + resolution for Custom Object fields. + +This feature is experimental, in particular, expect changes to the way the configuration is specified. +We'll probably extend the CLI instead of relying on environment variables in a future version. + +### Sample usage + +``` +PMD_VF_APEXDIRECTORIES=../classes \ +PMD_VF_OBJECTSDIRECTORIES=../objects \ +run.sh pmd -d $GITHUB_WORKSPACE/force-app/main/default/pages \ + -R category/vf/security.xml/VfUnescapeEl -f text +``` + +If you run with debug logging turned on, you might see log messages like this: + +``` +Okt. 14, 2021 11:30:44 AM net.sourceforge.pmd.lang.vf.VfExpressionTypeVisitor visit +FINE: Unable to determine type for: Account.NotFoundField__c +``` + +This means, that type resolution didn't work. Maybe the provided directories are missing or do not contain +the needed data. diff --git a/docs/pages/pmd/projectdocs/committers/merging_pull_requests.md b/docs/pages/pmd/projectdocs/committers/merging_pull_requests.md index 73df32671e..a450ff882d 100644 --- a/docs/pages/pmd/projectdocs/committers/merging_pull_requests.md +++ b/docs/pages/pmd/projectdocs/committers/merging_pull_requests.md @@ -1,7 +1,7 @@ --- title: Merging pull requests permalink: pmd_projectdocs_committers_merging_pull_requests.html -last_updated: August 2017 +last_updated: October 2021 author: Andreas Dangel --- @@ -9,10 +9,14 @@ author: Andreas Dangel 1. Review the pull request - * Compilation and checkstyle is verified already by travis build: PRs are automatically checked. - * If it is a bug fix, a new unit test, that reproduces the bug, is mandatory. Without such a test, we might accidentally reintroduce the bug again. - * Add the appropriate labels on the github issue: If the PR fixes a bug, the label "a:bug" should be used. - * Make sure, the PR is added to the appropriate milestone. If the PR fixes a bug, make sure, that the bug issue is added to the same milestone. + * Compilation and checkstyle is verified already by github actions build: + PRs are automatically checked. + * If it is a bug fix, a new unit test, that reproduces the bug, is mandatory. + Without such a test, we might accidentally reintroduce the bug again. + * Add the appropriate labels on the github issue: If the PR fixes a bug, the label "a:bug" + should be used. + * Make sure, the PR is added to the appropriate milestone. + If the PR fixes a bug, make sure, that the bug issue is added to the same milestone. 2. The actual merge commands: @@ -21,41 +25,62 @@ author: Andreas Dangel ``` git checkout master && git pull origin master # make sure, you have the latest code - git fetch origin pull/123/head:pr-123 && git checkout pr-123 # creates a new temporary branch + git fetch origin pull/123/head:pr-123 && git checkout pr-123 # creates a new temporary branch "pr-123" ``` 3. Update the [release notes](https://github.com/pmd/pmd/blob/master/docs/pages/release_notes.md): - + * Are there any API changes, that need to be documented? (Section "API Changes") * Are there any significant changes to existing rules, that should be mentioned? (Section "Modified Rules" / "New Rules" / "Removed Rules") + + Changes for modified rules are e.g. new properties or changed default values for properties. + * If the PR fixes a bug, make sure, it is listed under the section "Fixed Issues". - * In any case, add the PR to the section "External Contributions" + Also make sure, that the PR description mentions this (e.g. "- fixes #issue-number") and + the this PR is linked with the issue. Merging this PR will then automatically close the issue. + * In any case, add the PR to the section "External Contributions". * Commit these changes with the message: - - git add docs/pages/release_notes.md - git commit -m "Update release notes, refs #123" - + + ``` + git add docs/pages/release_notes.md + git commit -m "[doc] Update release notes (#123)" + ``` + {% include note.html content="If the PR fixes a bug, verify, that we have a commit with the message \"Fixes #issue-number\". If this doesn't exist, you can add it to the commit message when - updating the release notes: `Update release notes, refs #123, fixes #issue-number`. + updating the release notes: `[doc] Update release notes (#123, fixes #issue-number)`. This will automatically close the github issue." %} -4. Now merge the pull request into the master branch: +4. Add the contributor to `.all-contributorsrc`: + + ``` + npx all-contributors add + ``` + + And follow the instructions. This will create a new commit into to the current branch (pr-123) updating both + the file `.all-contributorsrc` and `docs/pages/pmd/projectdocs/credits.md`. + +5. Now merge the pull request into the master branch: ``` git checkout master - git merge --no-ff pr-123 + git merge --no-ff pr-123 -m "Merge pull request #123 from xyz:branch + + Full-title-of-the-pr #123" --log ``` {%include note.html content="If there are merge conflicts, you'll need to deal with them here." %} -5. Run the complete build: `./mvnw clean verify` +6. Run the complete build: `./mvnw clean verify -Pgenerate-rule-docs` {% include note.html content="This will execute all the unit tests and the checkstyle tests. It ensures, that the complete project can be build and is functioning on top of the current master." %} + + {% include note.html content="The profile `generate-rule-docs` will run the doc checks, that would + otherwise only run on github actions and fail the build, if e.g. a jdoc or rule reference is wrong." %} -6. If the build was successful, you are ready to push: +7. If the build was successful, you are ready to push: ``` git push origin master @@ -87,8 +112,8 @@ PMD version 5.8.0, so that we can create a bugfix release 5.8.1. ``` ./mvnw versions:set -DnewVersion=5.8.1-SNAPSHOT - git add pom.xml \*/pom.xml - git commit -m "prepare next version 5.8.1-SNAPSHOT" + git add pom.xml \*/pom.xml pmd-scala-modules/\*/pom.xml + git commit -m "Prepare next version 5.8.1-SNAPSHOT" ``` ### Merging the PR @@ -112,10 +137,12 @@ PMD version 5.8.0, so that we can create a bugfix release 5.8.1. ``` git checkout pmd/5.8.x - git merge --no-ff pr-124 + git merge --no-ff pr-124 -m "Merge pull request #124 from xyz:branch + + Full-title-of-the-pr #124" --log ``` -5. Just to be sure, run the complete build again: `./mvnw clean verify`. +5. Just to be sure, run the complete build again: `./mvnw clean verify -Pgenerate-rule-docs`. 6. If the build was successful, you are ready to push: diff --git a/docs/pages/pmd/projectdocs/committers/releasing.md b/docs/pages/pmd/projectdocs/committers/releasing.md index 49990b22e7..1cd90df180 100644 --- a/docs/pages/pmd/projectdocs/committers/releasing.md +++ b/docs/pages/pmd/projectdocs/committers/releasing.md @@ -99,7 +99,7 @@ NEW_VERSION_COMMITISH=HEAD echo "### Stats" echo "* $(git log pmd_releases/${LAST_VERSION}..${NEW_VERSION_COMMITISH} --oneline --no-merges |wc -l) commits" -echo "* $(curl -s https://api.github.com/repos/pmd/pmd/milestones|jq ".[] | select(.title == \"$NEW_VERSION\") | .closed_issues") closed tickets & PRs" +echo "* $(curl -s "https://api.github.com/repos/pmd/pmd/milestones?state=all&direction=desc&per_page=5"|jq ".[] | select(.title == \"$NEW_VERSION\") | .closed_issues") closed tickets & PRs" echo "* Days since last release: $(( ( $(date +%s) - $(git log --max-count=1 --format="%at" pmd_releases/${LAST_VERSION}) ) / 86400))" ``` diff --git a/docs/pages/pmd/projectdocs/credits.md b/docs/pages/pmd/projectdocs/credits.md index d1a771e9ec..275a220f5a 100644 --- a/docs/pages/pmd/projectdocs/credits.md +++ b/docs/pages/pmd/projectdocs/credits.md @@ -1,15 +1,949 @@ --- title: Credits permalink: pmd_projectdocs_credits.html -author: Tom Copeland --- -## Committers +## ✨ Contributors + +Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

0xflotus

💻 🐛

1henni

🐛

ALiNew

🐛

Abhijit Sarkar

🐛

Abhishek Kumar

🐛

Adam

🐛

Adam Carroll

🐛

Adam Obuchowicz

🐛

Adrian Price

🐛

Adrien Lecharpentier

🐛

Aidan Harding

🐛

Akshat Bahety

💻 🐛

Akshay Thapa

🐛

Alan Buttars

🐛

Alan Hohn

🐛

Alberto Fernández

💻 🐛

Alex Rentz

🐛

Alex Saveau

🐛

Alex Shesterov

💻 🐛

Alexey Markevich

🐛

Alexey Naumov

🐛

Alexey Yudichev

🐛

Alix

🐛

Alix

🐛

Amish Shah

🐛

Amit Prasad

🐛

Amitosh Swain Mahapatra

🐛

Anand Subramanian

💻 🐛

Anatoly Trosinenko

💻 🐛

Andi Pabst

💻 🐛

Andrea

🐛

Andrea Aime

🐛

Andreas Dangel

💻 📖 🐛 🚧

Andreas Markussen

🐛

Andreas Schmid

🐛

Andreas Turban

🐛

Andrei Paikin

🐛

Andrew

🐛

Andrew Green

🐛

Andrey Fomin

🐛

Andrey Hitrin

🐛

Andrey Mochalov

💻 🐛

Andro72

🐛

Andrwyw

🐛

Andrés Catalán

🐛

Andy Pattenden

🐛

Andy Ray

🐛

Andy Robinson

🐛

Andy-2639

🐛

Ankush Somani

🐛

Anmol Kumar

🐛

Anthony Whitford

🐛

AnthonyKot

🐛

Aravind Hegde

🐛

Arda Aslan

🐛

Ari Fogel

🐛

Arnaud Jeansen

💻 🐛

Arpit Koolwal

🐛

Artem

💻 🐛

Artem

🐛

Artem Sheremet

🐛

Artur

🐛

Artur Bosch

🐛

Artur Dryomov

🐛

Artur Ossowski

🐛

AshTheMash

🐛

Ashish Rana

🐛

Atul Kaushal

🐛

August Boland

🐛

Aurel Hudec

🐛

Austin Shalit

🐛

Austin Tice

🐛

Ayoub Kaanich

🐛

BBG

💻 📖 🐛

Barthélemy L.

🐛

Basavaraj K N

🐛

Basil Peace

🐛

Belle

🐛

Ben Lerner

🐛

Ben Manes

🐛

Ben McCann

🐛

Bendegúz Nagy

🐛

Bennet S Yee

🐛

Benoit Lacelle

🐛

Bernardo Macêdo

🐛

Bernd Farka

🐛

Betina Cynthia Mamani

🐛

Bhanu Prakash Pamidi

💻 🐛

Bhargav Thanki

🐛

Binu R J

🐛

Björn Kautler

💻 🐛

Blightbuster

🐛

Bo Zhang

🐛

Bob "Wombat" Hogg

🐛

Bobby Wertman

🐛

Bolarinwa Saheed Olayemi

💻 🐛

Boris Petrov

🐛

Brad Kent

🐛

Brandon Mikeska

🐛

Brian Batronis

🐛

Brian Johnson

🐛

Brice Dutheil

💻 🐛

Bruno Ferreira

🐛

Bruno Ritz

🐛

Cameron Donaldson

🐛

Carlos Macasaet

🐛

Carsten Otto

🐛

Charlie Housh

🐛

Charlie Jonas

🐛

Chas Honton

🐛

Chen Yang

🐛

Chotu

🐛

Chris Smith

🐛

Christian Hujer

🐛

Christian Pontesegger

🐛

ChristianWulf

🐛

Christofer Dutz

💻

Christoffer Anselm

🐛

Christophe Vidal

🐛

Christopher Dancy

🐛

Clemens Prill

🐛

Clint Chester

💻 🐛

Clément Fournier

💻 📖 🐛 🚧

Codacy Badger

🐛

Code-Nil

🐛

ColColonCleaner

🐛

Colin Ingarfield

🐛

Craig Andrews

🐛

Craig Muchinsky

🐛

Cyril

💻 🐛

Dale

💻

Damien Jiang

🐛

Dan Berindei

🐛

Dan Rollo

🐛

Dan Ziemba

🐛

Daniel Jipa

🐛

Daniel Reigada

🐛

Danilo Pianini

🐛

Darko

🐛

David

🐛

David Atkinson

🐛

David Burström

💻 🐛

David Goaté

🐛

David Golpira

🐛

David Kovařík

🐛

David M. Karr (fullname at gmail.com)

🐛

David Renz

💻 🐛

David Renz

🐛

Deleted user

🐛

Dell Green

🐛

Dem Pilafian

🐛

Den

🐛

Denis Borovikov

💻 🐛

Dennie Reniers

💻 🐛

Dennis Kieselhorst

🐛

Derek P. Moore

🐛

Dichotomia

🐛

Dionisio Cortés Fernández

💻 🐛

Dmitri Bourlatchkov

🐛

Dmitriy Kuzmin

🐛

Dmytro Dashenkov

🐛

Drew Hall

🐛

Dumitru Postoronca

🐛

Dylan Adams

🐛

Eden Hao

🐛

Egor Bredikhin

🐛

Elan P. Kugelmass

🐛

Elder S.

🐛

Emile

🐛

Eric

🐛

Eric Kintzer

🐛

Eric Perret

🐛

Eric Squires

🐛

Erich L Foster

🐛

Erik Bleske

🐛

Ernst Reissner

🐛

F.W. Dekker

🐛

Facundo

🐛

Federico Giust

🐛

Fedor Sherstobitov

🐛

Felix Lampe

🐛

Filip Golonka

🐛

Filipe Esperandio

💻 🐛

Francesco la Torre

🐛

Francisco Duarte

🐛

Frieder Bluemle

🐛

Frits Jalvingh

💻 🐛

G. Bazior

🐛

Gabe Henkes

🐛

Genoud Magloire

🐛

Geoffrey555

🐛

Georg Romstorfer

🐛

Gio

🐛

Gol

🐛

Gonzalo Exequiel Ibars Ingman

💻 🐛

GooDer

🐛

Gregor Riegler

🐛

Grzegorz Olszewski

🐛

Gunther Schrijvers

💻 🐛

Gustavo Krieger

🐛

Guy Elsmore-Paddock

🐛

Görkem Mülayim

🐛

Hanzel Godinez

🐛

Harsh Kukreja

🐛

Heber

🐛

Henning Schmiedehausen

💻 🐛

Henning von Bargen

💻

Hervé Boutemy

🐛

Himanshu Pandey

🐛

Hokwang Lee

🐛

Hooperbloob

💻

Hung PHAN

🐛

IDoCodingStuffs

💻 🐛

Iccen Gan

🐛

Ignacio Mariano Tirabasso

🐛

Igor Melnichenko

🐛

Igor Moreno

🐛

Intelesis-MS

🐛

Iroha_

🐛

Ishan Srivastava

🐛

Ivano Guerini

🐛

Ivar Andreas Bonsaksen

🐛

Ivo Šmíd

🐛

JJengility

🐛

Jake Hemmerle

🐛

James Harrison

🐛

Jan

🐛

Jan Aertgeerts

💻 🐛

Jan Brümmer

🐛

Jan Tříska

🐛

Jan-Lukas Else

🐛

Jason Williams

🐛

Jean-Paul Mayer

🐛

Jean-Simon Larochelle

🐛

Jeff Bartolotta

💻 🐛

Jeff Hube

💻 🐛

Jeff Jensen

🐛

Jeff May

🐛

Jens Gerdes

🐛

Jeroen Borgers

🐛

Jerome Russ

🐛

Jiri Pejchal

🐛

Jithin Sunny

🐛

Jiří Škorpil

🐛

Joao Machado

🐛

Jochen Krauss

🐛

Johan Hammar

🐛

John Karp

🐛

John Zhang

🐛

John-Teng

💻 🐛

Jon Moroney

💻 🐛

Jonas Geiregat

🐛

Jonathan Wiesel

💻 🐛

Jordan

🐛

Jordi Llach

🐛

Jorge Solórzano

🐛

JorneVL

🐛

Jose Palafox

🐛

Jose Stovall

🐛

Joseph

💻

Joseph Heenan

🐛

Josh Feingold

💻 🐛

Josh Holthaus

🐛

Joshua S Arquilevich

🐛

João Ferreira

💻 🐛

João Pedro Schmitt

🐛

Juan Martín Sotuyo Dodero

💻 📖 🐛 🚧

Juan Pablo Civile

🐛

Julian Voronetsky

🐛

Julien

🐛

Julius

🐛

JustPRV

🐛

Jörn Huxhorn

🐛

KThompso

🐛

Kai Amundsen

🐛

Karl-Andero Mere

🐛

Karl-Philipp Richter

🐛

Karsten Silz

🐛

Kazuma Watanabe

🐛

Kev

🐛

Keve Müller

🐛

Kevin Guerra

💻

Kevin Jones

🐛

Kevin Wayne

🐛

Kieran Black

🐛

Kirill Zubov

🐛

Kirk Clemens

💻 🐛

Klaus Hartl

🐛

Koen Van Looveren

🐛

Kris Scheibe

💻 🐛

Kunal Thanki

🐛

Larry Diamond

💻 🐛

Lars Knickrehm

🐛

Leo Gutierrez

🐛

Lintsi

🐛

Linus Fernandes

🐛

Lixon Lookose

🐛

Logesh

🐛

Lorenzo Gabriele

🐛

Loïc Ledoyen

🐛

Lucas Silva

🐛

Lucas Soncini

💻 🐛

Lukasz Slonina

🐛

Lukebray

🐛

Lyor Goldstein

🐛

MCMicS

🐛

Macarse

🐛

Machine account for PMD

💻

Maciek Siemczyk

🐛

Maikel Steneker

💻 🐛

Maksim Moiseikin

🐛

Manfred Koch

🐛

Manuel Moya Ferrer

💻 🐛

Manuel Ryan

🐛

Marat Vyshegorodtsev

🐛

Marcel Härle

🐛

Marcello Fialho

🐛

Marcin Rataj

🐛

Mark Adamcin

🐛

Mark Hall

💻 🐛

Mark Kolich

🐛

Mark Pritchard

🐛

Markus Rathgeb

🐛

Marquis Wang

🐛

Martin Feldsztejn

🐛

Martin Lehmann

🐛

Martin Spamer

🐛

Martin Tarjányi

🐛

MatFl

🐛

Mateusz Stefanski

🐛

Mathieu Gouin

🐛

MatiasComercio

💻 🐛

Matt Benson

🐛

Matt De Poorter

🐛

Matt Harrah

🐛

Matt Nelson

🐛

Matthew Amos

🐛

Matthew Duggan

🐛

Matthew Hall

🐛

Matías Fraga

💻 🐛

Maxime Robert

💻 🐛

Michael

🐛

Michael Bell

🐛

Michael Bernstein

🐛

Michael Clay

🐛

Michael Dombrowski

🐛

Michael Hausegger

🐛

Michael Hoefer

🐛

Michael Möbius

🐛

Michael N. Lipp

🐛

Michael Pellegrini

🐛

Michal Kordas

🐛

Michał Borek

🐛

Michał Kuliński

🐛

Miguel Núñez Díaz-Montes

🐛

Mihai Ionut

🐛

Mirek Hankus

🐛

Mladjan Gadzic

🐛

MrAngry52

🐛

Muminur Choudhury

🐛

Mykhailo Palahuta

💻 🐛

Nagendra Kumar Singh

🐛

Nahuel Barrios

🐛

Nathan Braun

🐛

Nathan Reynolds

🐛

Nathan Reynolds

🐛

Nathanaël

🐛

Nazdravi

🐛

Neha-Dhonde

🐛

Nicholas Doyle

🐛

Nick Butcher

🐛

Nico Gallinal

🐛

Nicola Dal Maso

🐛

Nicolas Filotto

💻

Nikita Chursin

🐛

Niklas Baudy

🐛

Nikolas Havrikov

🐛

Nilesh Virkar

🐛

Nimit Patel

🐛

Niranjan Harpale

🐛

Noah Sussman

🐛

Noah0120

🐛

Noam Tamim

🐛

Noel Grandin

🐛

Olaf Haalstra

🐛

Oleg Pavlenko

🐛

Oliver Eikemeier

🐛

Olivier Parent

💻 🐛

Ollie Abbey

💻 🐛

OverDrone

🐛

Ozan Gulle

💻 🐛

PUNEET JAIN

🐛

Parbati Bose

🐛

Paul Berg

🐛

Pavel Bludov

🐛

Pavel Mička

🐛

Pedro Nuno Santos

🐛

Pedro Rijo

🐛

Pelisse Romain

💻 📖 🐛

Pete Davids

🐛

Peter Bruin

🐛

Peter Chittum

💻 🐛

Peter Cudmore

🐛

Peter Kasson

🐛

Peter Kofler

🐛

Pham Hai Trung

🐛

Philip Graf

💻 🐛

Philip Hachey

🐛

Philippe Ozil

🐛

Phinehas Artemix

🐛

Phokham Nonava

🐛

Piotr Szymański

🐛

Piotrek Żygieło

💻 🐛

Pranay Jaiswal

🐛

Prasad Kamath

🐛

Prasanna

🐛

Presh-AR

🐛

Puneet1726

🐛

Rafael Cortês

🐛

RaheemShaik999

🐛

RajeshR

💻 🐛

Ramachandra Mohan

🐛

Raquel Pau

🐛

Ravikiran Janardhana

🐛

Reda Benhemmouche

🐛

Renato Oliveira

💻 🐛

Rich DiCroce

🐛

Riot R1cket

🐛

Rishabh Jain

🐛

RishabhDeep Singh

🐛

Robbie Martinus

💻 🐛

Robert Henry

🐛

Robert Painsi

🐛

Robert Russell

🐛

Robert Sösemann

💻 📖 📢 🐛

Robert Whitebit

🐛

Robin Richtsfeld

🐛

Robin Stocker

💻 🐛

Robin Wils

🐛

RochusOest

🐛

Rodolfo Noviski

🐛

Rodrigo Casara

🐛

Rodrigo Fernandes

🐛

Roman Salvador

💻 🐛

Ronald Blaschke

🐛

Róbert Papp

🐛

Saikat Sengupta

🐛

Saksham Handu

🐛

Saladoc

🐛

Salesforce Bob Lightning

🐛

Sam Carlberg

🐛

Satoshi Kubo

🐛

Scott Kennedy

🐛

Scott Wells

🐛 💻

Sebastian Bögl

🐛

Sebastian Schuberth

🐛

Sebastian Schwarz

🐛

Sergey Gorbaty

🐛

Sergey Kozlov

🐛

Sergey Yanzin

💻 🐛

Shubham

💻 🐛

Simon Xiao

🐛

Srinivasan Venkatachalam

🐛

Stanislav Gromov

🐛

Stanislav Myachenkov

💻

Stefan Birkner

🐛

Stefan Bohn

🐛

Stefan Endrullis

🐛

Stefan Klöss-Schuster

🐛

Stefan Wolf

🐛

Stephan H. Wissel

🐛

Stephen

🐛

Stephen Friedrich

🐛

Stexxe

🐛

Stian Lågstad

🐛

StuartClayton5

🐛

Supun Arunoda

🐛

Suren Abrahamyan

🐛

SwatiBGupta1110

🐛

SyedThoufich

🐛

Szymon Sasin

🐛

T-chuangxin

🐛

TERAI Atsuhiro

🐛

TIOBE Software

💻 🐛

Taylor Smock

🐛

Techeira Damián

💻 🐛

Ted Husted

🐛

TehBakker

🐛

The Gitter Badger

🐛

Theodoor

🐛

Thiago Henrique Hüpner

🐛

Thibault Meyer

🐛

Thomas Güttler

🐛

Thomas Jones-Low

🐛

Thomas Smith

💻 🐛

ThrawnCA

🐛

Thunderforge

💻 🐛

Tim van der Lippe

🐛

Tobias Weimer

💻 🐛

Tom Daly

🐛

Tomer Figenblat

🐛

Tomi De Lucca

💻 🐛

Torsten Kleiber

🐛

TrackerSB

🐛

Ullrich Hafner

🐛

Utku Cuhadaroglu

💻 🐛

Valentin Brandl

🐛

Valeria

🐛

Vasily Anisimov

🐛

Vickenty Fesunov

🐛

Victor Noël

🐛

Vincent HUYNH

🐛

Vincent Maurin

🐛

Vincent Privat

🐛

Vishhwas

🐛

Vitaly

🐛

Vitaly Polonetsky

🐛

Vojtech Polivka

🐛

Vsevolod Zholobov

🐛

Wang Shidong

🐛

Waqas Ahmed

🐛

Wayne J. Earl

🐛

Wchenghui

🐛

Will Winder

🐛

William Brockhus

💻 🐛

Wilson Kurniawan

🐛

Wim Deblauwe

🐛

Woongsik Choi

🐛

XenoAmess

💻 🐛

Yang

💻

YaroslavTER

🐛

Young Chan

💻 🐛

YuJin Kim

🐛

Yuri Dolzhenko

🐛

Yurii Dubinka

🐛

Zoltan Farkas

🐛

Zustin

🐛

aaronhurst-google

🐛

alexmodis

🐛

andreoss

🐛

andrey81inmd

💻 🐛

anicoara

🐛

arunprasathav

🐛

asiercamara

🐛

astillich-igniti

💻

avesolovksyy

🐛

avishvat

🐛

avivmu

🐛

axelbarfod1

🐛

b-3-n

🐛

balbhadra9

🐛

base23de

🐛

bergander

🐛

berkam

💻 🐛

breizh31

🐛

caesarkim

🐛

carolyujing

🐛

cesares-basilico

🐛

chrite

🐛

cobratbq

🐛

coladict

🐛

cosmoJFH

🐛

cristalp

🐛

crunsk

🐛

cwholmes

🐛

cyberjj999

🐛

cyw3

🐛

d1ss0nanz

🐛

danbrycefairsailcom

🐛

dariansanity

🐛

darrenmiliband

🐛

davidburstrom

🐛

dbirkman-paloalto

🐛

deepak-patra

🐛

dependabot[bot]

💻 🐛

dinesh150

🐛

diziaq

🐛

dreaminpast123

🐛

duanyanan

🐛

dutt-sanjay

🐛

dylanleung

🐛

dzeigler

🐛

ekkirala

🐛

emersonmoura

🐛

fairy

🐛

foxmason

🐛

frankegabor

🐛

frankl

🐛

freafrea

🐛

fsapatin

🐛

gracia19

🐛

guo fei

🐛

gurmsc5

🐛

gwilymatgearset

💻 🐛

haigsn

🐛

hemanshu070

🐛

henrik242

🐛

hongpuwu

🐛

hvbtup

💻 🐛

igniti GmbH

🐛

ilovezfs

🐛

itaigilo

🐛

jakivey32

🐛

jbennett2091

🐛

jcamerin

🐛

jkeener1

🐛

jmetertea

🐛

johnra2

💻

josemanuelrolon

💻 🐛

kabroxiko

💻 🐛

karwer

🐛

kaulonline

🐛

kdaemonv

🐛

kenji21

💻 🐛

kfranic

🐛

khalidkh

🐛

krzyk

🐛

lasselindqvist

🐛

lihuaib

🐛

lonelyma1021

🐛

lpeddy

🐛

lujiefsi

💻

lyriccoder

🐛

marcelmore

🐛

matchbox

🐛

matthiaskraaz

🐛

meandonlyme

🐛

mikesive

🐛

milossesic

🐛

mriddell95

🐛

mrlzh

🐛

msloan

🐛

mucharlaravalika

🐛

mvenneman

🐛

nareshl119

🐛

nicolas-harraudeau-sonarsource

🐛

noerremark

🐛

novsirion

🐛

oggboy

🐛

oinume

🐛

orimarko

💻 🐛

pallavi agarwal

🐛

parksungrin

🐛

patpatpat123

🐛

patriksevallius

🐛

pbrajesh1

🐛

phoenix384

🐛

piotrszymanski-sc

💻

plan3d

🐛

poojasix

🐛

prabhushrikant

🐛

pujitha8783

🐛

r-r-a-j

🐛

raghujayjunk

🐛

rajeshveera

🐛

rajeswarreddy88

🐛

recdevs

🐛

reudismam

💻 🐛

rijkt

🐛

rillig-tk

🐛

rmohan20

💻 🐛

rxmicro

🐛

ryan-gustafson

💻 🐛

sabi0

🐛

scais

🐛

sebbASF

🐛

sergeygorbaty

💻

shilko2013

🐛

simeonKondr

🐛

snajberk

🐛

sniperrifle2004

🐛

snuyanzin

🐛

sratz

🐛

stonio

🐛

sturton

💻 🐛

sudharmohan

🐛

suruchidawar

🐛

svenfinitiv

🐛

tashiscool

🐛

test-git-hook

🐛

testation21

💻 🐛

thanosa

🐛

tiandiyixian

🐛

tobwoerk

🐛

tprouvot

🐛

trentchilders

🐛

triandicAnt

🐛

trishul14

🐛

tsui

🐛

winhkey

🐛

witherspore

🐛

wjljack

🐛

wuchiuwong

🐛

xingsong

🐛

xioayuge

🐛

xnYi9wRezm

💻 🐛

xuanuy

🐛

xyf0921

🐛

yalechen-cyw3

🐛

yasuharu-sato

🐛

zenglian

🐛

zgrzyt93

💻 🐛

zh3ng

🐛

zt_soft

🐛

ztt79

🐛

zzzzfeng

🐛

Árpád Magosányi

🐛

任贵杰

🐛
+ + + + + + +
+ +## Previous Contributors + +These are collected contributors before we moved to github. + +### Committers * David Dixon-Peugh - PMD core, much of the early work on the grammar, initial Emacs plugin * Philippe Herlin - Eclipse plugin, fixed bugs in RuleSetFactory * Nascif Abousalh Neto - Emacs plugin -* [Tom Copeland](http://tomcopeland.blogs.com/) - PMD core, lead developer, JDeveloper plugin, initial Gel plugin, +* [Tom Copeland](https://thomasleecopeland.com/) - PMD core, lead developer, JDeveloper plugin, initial Gel plugin, initial jEdit plugin, IDEAJ integration, BlueJ extension * Jiger Patel - jEdit plugin * Alan Ezust - jEdit plugin @@ -35,7 +969,7 @@ author: Tom Copeland RemoteInterfaceNamingConvention, AvoidFinalLocalVariable, ClassWithOnlyPrivateConstructorsShouldBeFinal, TooManyStaticImports, DoNotCallSystemExit, StaticEJBFieldShouldBeFinal -## Committers emeritus +### Committers emeritus * Gunnlaugur Thor Briem - NetBeans plugin, Maven build script fixes, bug report on JavaCC parser's use of java.lang.Error @@ -49,7 +983,7 @@ author: Tom Copeland * Colin Wilson-Salt - NetBeans plugin team * [Brant Gurganus](http://gurganus.name/brant/) - JCreator integration, Swing GUI work -## Significant contributors +### Significant contributors * Pieter Vanraemdonck - JSP grammar/integration/documentation, DontNestJsfInJstlIteration, NoLongScripts, NoScriptlets, NoInlineStyleInformation, NoClassAttribute, NoJspForward @@ -58,7 +992,7 @@ author: Tom Copeland * Daniel Sheppard - XPath engine integration concept and implementation, advice on Jaxen extension function naming * Brian Ewins - complete rewrite of CPD based on the Burrows-Wheeler transform, fixed DocumentNavigator bug -## Contributors +### Contributors * Andy Throgmorton - New XPath getCommentOn function, new rule DontCallThreadRun, fix for rule UseArraysAsList * Nicolas Dordet - Fixed an issue on CloseResource @@ -428,7 +1362,7 @@ author: Tom Copeland * Mat Booth - #1109 Patch to build with Javacc 5.0 * Stuart Turton - for PLSQL support. See also [pldoc](http://pldoc.sourceforge.net/) * Andrey Utis - for adding Apache Velocity as a new language and writing up a - [howto for adding new languages](pmd_devdocs_major_adding_new_language.html). + [howto for adding new languages JavaCC](pmd_devdocs_major_adding_new_language_javacc.html). * Alan Hohn - for adding Standard and modified cyclomatic complexity rules * Jan van Nunen - for adding CPD support for Matlab, Objective-C, Python, Scala and various bug fixes * Juan Martín Sotuyo Dodero - for many bugfixes/pull requests improving Java grammar and performance diff --git a/docs/pages/pmd/userdocs/cli_reference.md b/docs/pages/pmd/userdocs/cli_reference.md index 541f49eb25..9ada368c91 100644 --- a/docs/pages/pmd/userdocs/cli_reference.md +++ b/docs/pages/pmd/userdocs/cli_reference.md @@ -10,7 +10,7 @@ author: Tom Copeland , Xavier Le Vourch @@ -20,24 +20,24 @@ The tool comes with a rather extensive help text, simply running with `-help`! Applies to - {% include custom/cli_option_row.html options="-rulesets,-R" + {% include custom/cli_option_row.html options="--rulesets,-R" option_arg="refs" description="Comma-separated list of ruleset or rule references." required="yes" %} - {% include custom/cli_option_row.html options="-dir,-d" + {% include custom/cli_option_row.html options="--dir,-d" option_arg="path" description="Root directory for the analyzed sources." required="yes" %} - {% include custom/cli_option_row.html options="-format,-f" + {% include custom/cli_option_row.html options="--format,-f" option_arg="format" description="Output format of the analysis report. The available formats are described [here](#available-report-formats)." default="text" %} - {% include custom/cli_option_row.html options="-auxclasspath" + {% include custom/cli_option_row.html options="--aux-classpath" option_arg="cp" description="Specifies the classpath for libraries used by the source code. This is used to resolve types in source files. The platform specific path delimiter @@ -46,11 +46,11 @@ The tool comes with a rather extensive help text, simply running with `-help`! to a text file containing path elements on consecutive lines can be specified." languages="Java" %} - {% include custom/cli_option_row.html options="-benchmark,-b" + {% include custom/cli_option_row.html options="--benchmark,-b" description="Enables benchmark mode, which outputs a benchmark report upon completion. The report is sent to standard error." %} - {% include custom/cli_option_row.html options="-cache" + {% include custom/cli_option_row.html options="--cache" option_arg="filepath" description="Specify the location of the cache file for incremental analysis. This should be the full path to the file, including the desired file name (not just the parent directory). @@ -58,86 +58,100 @@ The tool comes with a rather extensive help text, simply running with `-help`! with the most up-to-date rule violations. This can greatly improve analysis performance and is **highly recommended**." %} - {% include custom/cli_option_row.html options="-debug,-verbose,-D,-V" + {% include custom/cli_option_row.html options="--debug,--verbose,-D,-V" description="Debug mode. Prints more log output." %} - {% include custom/cli_option_row.html options="-encoding,-e" + {% include custom/cli_option_row.html options="--encoding,-e" option_arg="charset" description="Specifies the character set encoding of the source code files PMD is reading. The valid values are the standard character sets of `java.nio.charset.Charset`." default="UTF-8" %} - {% include custom/cli_option_row.html options="-failOnViolation,--failOnViolation" + {% include custom/cli_option_row.html options="--fail-on-violation" option_arg="bool" description="Specifies whether PMD exits with non-zero status if violations are found. By default PMD exits with status 4 if violations are found. - Disable this feature with `-failOnViolation false` to exit with 0 instead and just output the report." + Disable this feature with `--fail-on-violation false` to exit with 0 instead and just output the report." default="true" %} - {% include custom/cli_option_row.html options="-filelist" + {% include custom/cli_option_row.html options="--file-list" option_arg="filepath" description="Path to file containing a comma delimited list of files to analyze. - If this is given, then you don't need to provide `-dir`." + If this is given, then you don't need to provide `--dir`." %} - {% include custom/cli_option_row.html options="-ignorelist" + {% include custom/cli_option_row.html options="--force-language" + option_arg="lang" + description="Force a language to be used for all input files, irrespective of + filenames. When using this option, the automatic language selection + by extension is disabled and all files are tried to be parsed with + the given language `<lang>`. Parsing errors are ignored and unparsable files + are skipped. + +

This option allows to use the xml language for files, that don't + use xml as extension. See [example](#analyze-other-xml-formats) below.

" + %} + {% include custom/cli_option_row.html options="--ignore-list" option_arg="filepath" description="Path to file containing a comma delimited list of files to ignore. - This option can be combined with `-dir` and `-filelist`. + This option can be combined with `--dir` and `--file-list`. This ignore list takes precedence over any files in the filelist." %} - {% include custom/cli_option_row.html options="-help,-h,-H" + {% include custom/cli_option_row.html options="--help,-h,-H" description="Display help on usage." %} {% include custom/cli_option_row.html options="-language,-l" option_arg="lang" description="Specify the language PMD should use. Used together with `-version`. See also [Supported Languages](#supported-languages)." %} - {% include custom/cli_option_row.html options="-minimumpriority,-min" + {% include custom/cli_option_row.html options="--minimum-priority,-min" option_arg="num" description="Rule priority threshold; rules with lower priority than configured here won't be used." default="5" %} - {% include custom/cli_option_row.html options="-norulesetcompatibility" + {% include custom/cli_option_row.html options="--no-ruleset-compatibility" description='Disable automatic fixing of invalid rule references. Without the switch, PMD tries to automatically replace rule references that point to moved or renamed rules with the newer location if possible. Disabling it is not recommended.' %} - {% include custom/cli_option_row.html options="-no-cache" + {% include custom/cli_option_row.html options="--no-cache" description="Explicitly disables incremental analysis. This switch turns off suggestions to use Incremental Analysis, - and causes the `-cache` option to be discarded if it is provided." + and causes the `--cache` option to be discarded if it is provided." %} - {% include custom/cli_option_row.html options="-property,-P" + {% include custom/cli_option_row.html options="--property,-P" option_arg="name>= 0Everything is fine, no violations found 1Couldn't understand command-line parameters or PMD exited with an exception -4At least one violation has been detected, unless -failOnViolation false is set. +4At least one violation has been detected, unless --fail-on-violation false is set. @@ -185,7 +199,7 @@ Example: * [apex](pmd_rules_apex.html) (Salesforce Apex) * [java](pmd_rules_java.html) * Supported Versions: 1.3, 1.4, 1.5, 5, 1.6, 6, 1.7, 7, 1.8, 8, 9, 1.9, 10, 1.10, 11, 12, - 13, 14, 14-preview, 15 (default), 15-preview + 13, 14, 15, 16, 16-preview, 17 (default), 17-preview * [ecmascript](pmd_rules_ecmascript.html) (JavaScript) * [jsp](pmd_rules_jsp.html) * [modelica](pmd_rules_modelica.html) @@ -202,3 +216,26 @@ Example: PMD comes with many different renderers. All formats are described at [PMD Report formats](pmd_userdocs_report_formats.html) +## Examples + +### Analyze other xml formats + +If your xml language doesn't use `xml` as file extension, you can still use PMD with `--force-language`: + +``` +$ ./run.sh pmd -d /home/me/src/xml-file.ext -f text -R ruleset.xml --force-language xml +``` + +You can also specify a directory instead of a single file. Then all files are analyzed. In that case, +parse errors are suppressed in order to reduce irrelevant noise: + +``` +$ ./run.sh pmd -d /home/me/src/ -f text -R ruleset.xml --force-language xml +``` + +Alternatively, you can create a filelist to only analyze files with a given extension: + +``` +$ find /home/me/src -name "*.ext" > /home/me/src/filelist.txt +$ ./run.sh pmd --file-list /home/me/src/filelist.txt -f text -R ruleset.xml --force-language xml +``` diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index c5e142341d..dc4577e41d 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -66,7 +66,7 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt description="List of files and directories to process" required="yes" %} - {% include custom/cli_option_row.html options="--filelist" + {% include custom/cli_option_row.html options="--file-list" description="Path to file containing a comma delimited list of files to analyze. If this is given, then you don't need to provide `--files`." %} {% include custom/cli_option_row.html options="--language" @@ -95,10 +95,10 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt description="Report format." default="text" %} - {% include custom/cli_option_row.html options="--failOnViolation" + {% include custom/cli_option_row.html options="--fail-on-violation" option_arg="bool" description="By default CPD exits with status 4 if code duplications are found. - Disable this option with `--failOnViolation false` to exit with 0 instead and just write the report." + Disable this option with `--fail-on-violation false` to exit with 0 instead and just write the report." default="true" %} {% include custom/cli_option_row.html options="--ignore-literals" @@ -206,7 +206,7 @@ This behavior has been introduced to ease CPD integration into scripts or hooks, - +
0Everything is fine, no code duplications found
1Couldn't understand command line parameters or CPD exited with an exception
4At least one code duplication has been detected unless '--failOnViolation false' is used.
4At least one code duplication has been detected unless '--fail-on-violation false' is used.
diff --git a/docs/pages/pmd/userdocs/installation.md b/docs/pages/pmd/userdocs/installation.md index 2374155cf6..c50dabc63d 100644 --- a/docs/pages/pmd/userdocs/installation.md +++ b/docs/pages/pmd/userdocs/installation.md @@ -63,7 +63,7 @@ modifiers on Java sources with `-R category/java/codestyle.xml/UnnecessaryModifi Additionally, the following options, are specified most of the time even though they're not required: * `-f `: report format. PMD supports many report formats out of the box. You may want to start with the basic `text` format (default) or `xml` format. The supported formats are [documented here](pmd_userdocs_cli_reference.html#available-report-formats). -* `-auxclasspath `: class path containing the compiled class files of the analysed Java sources, if any. +* `--aux-classpath `: class path containing the compiled class files of the analysed Java sources, if any. Setting this up correctly allows PMD to do much deeper analysis using reflection. Some rules, such as [MissingOverride](pmd_rules_java_bestpractices.html#missingoverride), require it to function properly. diff --git a/docs/pages/pmd/userdocs/tools/ant.md b/docs/pages/pmd/userdocs/tools/ant.md index b34820e1f0..9eaf415b6b 100644 --- a/docs/pages/pmd/userdocs/tools/ant.md +++ b/docs/pages/pmd/userdocs/tools/ant.md @@ -224,9 +224,12 @@ nested element. Possible values are: - - - + + + + + + diff --git a/docs/pages/pmd/userdocs/tools/tools.md b/docs/pages/pmd/userdocs/tools/tools.md index b3e8d550c9..2d35030e81 100644 --- a/docs/pages/pmd/userdocs/tools/tools.md +++ b/docs/pages/pmd/userdocs/tools/tools.md @@ -20,6 +20,18 @@ With Codacy you have PMDJava analysis out-of-the-box, and it is free for open so * Source code: [https://github.com/codacy/codacy-pmdjava](https://github.com/codacy/codacy-pmdjava) * Maintainer: Codacy +### Codiga + +[Codiga](https://www.codiga.io) automates code review, check your code quality and helps you manage your technical debt. +It is integrated with GitHub, GitLab and Bitbucket. The platform also analyzes code directly in your IDE using its integration +plugins for VS Code and IntelliJ, providing a consistent analysis along your development cycle (from the IDE to the CI/CD pipeline). + +Codiga uses PMD to check Java and Apex code. + +* Homepage: [https://www.codiga.io](https://www.codiga.io) +* Documentation: [https://doc.codiga.io](https://doc.codiga.io) + + ## IDE Integrations ### Summary diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 8abb75f161..b6264a6b19 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -21,9 +21,14 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* java-performance + * [#3492](https://github.com/pmd/pmd/issues/3492): \[java] UselessStringValueOf: False positive when there is no initial String to append to + ### API Changes ### External Contributions +* [#3631](https://github.com/pmd/pmd/pull/3631): \[java] Fixed False positive for UselessStringValueOf when there is no initial String to append to - [John Armgardt](https://github.com/johnra2) + {% endtocmaker %} diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index 807d854bd1..e76bd1493e 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -5,6 +5,571 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: https://github.com/pmd/pmd/releases +## 27-November-2021 - 6.41.0 + +The PMD team is pleased to announce PMD 6.41.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [GitHub Action for PMD](#github-action-for-pmd) + * [Last release in 2021](#last-release-in-2021) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) + * [Command Line Interface](#command-line-interface) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +#### GitHub Action for PMD + +PMD now has its own official GitHub Action: [GitHub Action for PMD](https://github.com/marketplace/actions/pmd). +It can execute PMD with your own ruleset against your project. It creates a [SARIF](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html) +report which is uploaded as a build artifact. Furthermore the build can be failed based on the number of violations. + +Feedback and pull requests are welcome at . + +#### Last release in 2021 + +This minor release will be the last one in 2021. The next release is scheduled to be end of January 2022. + +### Fixed Issues + +* core + * [#2954](https://github.com/pmd/pmd/issues/2954): Create GitHub Action for PMD + * [#3424](https://github.com/pmd/pmd/issues/3424): \[core] Migrate CLI to using GNU-style long options + * [#3425](https://github.com/pmd/pmd/issues/3425): \[core] Add a `--version` CLI option + * [#3593](https://github.com/pmd/pmd/issues/3593): \[core] Ant task fails with Java17 + * [#3635](https://github.com/pmd/pmd/issues/3635): \[ci] Update sample projects for regression tester +* java-bestpractices + * [#3595](https://github.com/pmd/pmd/issues/3595): \[java] PrimitiveWrapperInstantiation: no violation on 'new Boolean(val)' + * [#3613](https://github.com/pmd/pmd/issues/3613): \[java] ArrayIsStoredDirectly doesn't consider nested classes + * [#3614](https://github.com/pmd/pmd/issues/3614): \[java] JUnitTestsShouldIncludeAssert doesn't consider nested classes + * [#3618](https://github.com/pmd/pmd/issues/3618): \[java] UnusedFormalParameter doesn't consider anonymous classes + * [#3630](https://github.com/pmd/pmd/issues/3630): \[java] MethodReturnsInternalArray doesn't consider anonymous classes +* java-design + * [#3620](https://github.com/pmd/pmd/issues/3620): \[java] SingularField doesn't consider anonymous classes defined in non-private fields +* java-errorprone + * [#3624](https://github.com/pmd/pmd/issues/3624): \[java] TestClassWithoutTestCases reports wrong classes in a file +* java-performance + * [#3491](https://github.com/pmd/pmd/issues/3491): \[java] UselessStringValueOf: False positive when `valueOf(char [], int, int)` is used + +### API Changes + +#### Command Line Interface + +The command line options for PMD and CPD now use GNU-syle long options format. E.g. instead of `-rulesets` the +preferred usage is now `--rulesets`. Alternatively one can still use the short option `-R`. +Some options also have been renamed to a more consistent casing pattern at the same time +(`--fail-on-violation` instead of `-failOnViolation`). +The old single-dash options are still supported but are deprecated and will be removed with PMD 7. +This change makes the command line interface more consistent within PMD and also less surprising +compared to other cli tools. + +The changes in detail for PMD: + +|old option |new option| +|-------------------------------|----------| +| `-rulesets` | `--rulesets` (or `-R`) | +| `-uri` | `--uri` | +| `-dir` | `--dir` (or `-d`) | +| `-filelist` | `--file-list` | +| `-ignorelist` | `--ignore-list` | +| `-format` | `--format` (or `-f`) | +| `-debug` | `--debug` | +| `-verbose` | `--verbose` | +| `-help` | `--help` | +| `-encoding` | `--encoding` | +| `-threads` | `--threads` | +| `-benchmark` | `--benchmark` | +| `-stress` | `--stress` | +| `-shortnames` | `--short-names` | +| `-showsuppressed` | `--show-suppressed` | +| `-suppressmarker` | `--suppress-marker` | +| `-minimumpriority` | `--minimum-priority` | +| `-property` | `--property` | +| `-reportfile` | `--report-file` | +| `-force-language` | `--force-language` | +| `-auxclasspath` | `--aux-classpath` | +| `-failOnViolation` | `--fail-on-violation` | +| `--failOnViolation` | `--fail-on-violation` | +| `-norulesetcompatibility` | `--no-ruleset-compatibility` | +| `-cache` | `--cache` | +| `-no-cache` | `--no-cache` | + +The changes in detail for CPD: + +|old option |new option| +|-----------------------|----------| +| `--failOnViolation` | `--fail-on-violation` | +| `-failOnViolation` | `--fail-on-violation` | +| `--filelist` | `--file-list` | + +### External Contributions + +* [#3600](https://github.com/pmd/pmd/pull/3600): \[core] Implement GNU-style long options and '--version' - [Yang](https://github.com/duanyang25) +* [#3612](https://github.com/pmd/pmd/pull/3612): \[java] Created fix for UselessStringValueOf false positive - [John Armgardt](https://github.com/johnra2) +* [#3648](https://github.com/pmd/pmd/pull/3648): \[doc] Rename Code Inspector to Codiga - [Julien Delange](https://github.com/juli1) + +### Stats +* 80 commits +* 23 closed tickets & PRs +* Days since last release: 28 + +## 30-October-2021 - 6.40.0 + +The PMD team is pleased to announce PMD 6.40.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [Updated Apex Support](#updated-apex-support) + * [New rules](#new-rules) + * [Modified rules](#modified-rules) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) + * [Experimental APIs](#experimental-apis) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +#### Updated Apex Support + +* The Apex language support has been bumped to version 54.0 (Spring '22). + +#### New rules + +* The new Apex rule [`EagerlyLoadedDescribeSObjectResult`](https://pmd.github.io/pmd-6.41.0-SNAPSHOT/pmd_rules_apex_performance.html#eagerlyloadeddescribesobjectresult) finds + `DescribeSObjectResult`s which could have been loaded eagerly via `SObjectType.getDescribe()`. + +```xml + +``` + +#### Modified rules + +* The Apex rule [`ApexUnitTestClassShouldHaveAsserts`](https://pmd.github.io/pmd-6.41.0-SNAPSHOT/pmd_rules_apex_bestpractices.html#apexunittestclassshouldhaveasserts) has a new property + `additionalAssertMethodPattern`. When specified the pattern is evaluated against each invoked + method name to determine whether it represents a test assertion in addition to the standard names. + +* The Apex rule [`ApexDoc`](https://pmd.github.io/pmd-6.41.0-SNAPSHOT/pmd_rules_apex_documentation.html#apexdoc) has a new property `reportMissingDescription`. + If set to `false` (default is `true` if unspecified) doesn't report an issue if the `@description` + tag is missing. This is consistent with the ApexDoc dialect supported by derivatives such as + [SfApexDoc](https://gitlab.com/StevenWCox/sfapexdoc) and also with analogous documentation tools for + other languages, e.g., JavaDoc, ESDoc/JSDoc, etc. + +* The Apex rule [`ApexCRUDViolation`](https://pmd.github.io/pmd-6.41.0-SNAPSHOT/pmd_rules_apex_security.html#apexcrudviolation) has a couple of new properties: + These allow specification of regular-expression-based patterns for additional methods that should + be considered valid for pre-CRUD authorization beyond those offered by the system Apex checks and + ESAPI, e.g., [`sirono-common`'s `AuthorizationUtil` class](https://github.com/SCWells72/sirono-common#authorization-utilities). + Two new properties have been added per-CRUD operation, one to specify the naming pattern for a method + that authorizes that operation and another to specify the argument passed to that method that contains + the `SObjectType` instance of the type being authorized. Here is an example of these new properties: + + ```xml + + 3 + + + + + + + + + + + + ``` + +* The Apex rule [`EmptyStatementBlock`](https://pmd.github.io/pmd-6.41.0-SNAPSHOT/pmd_rules_apex_errorprone.html#emptystatementblock) has two new properties: + + Setting `reportEmptyPrivateNoArgConstructor` to `false` ignores empty private no-arg constructors + that are commonly used in singleton pattern implementations and utility classes in support of + prescribed best practices. + + Setting `reportEmptyVirtualMethod` to `false` ignores empty virtual methods that are commonly used in + abstract base classes as default no-op implementations when derived classes typically only override a + subset of virtual methods. + + By default, both properties are `true` to not change the default behaviour of this rule. + +* The Apex rule [`EmptyCatchBlock`](https://pmd.github.io/pmd-6.41.0-SNAPSHOT/pmd_rules_apex_errorprone.html#emptycatchblock) has two new properties modeled after the analgous Java rule: + + The `allowCommentedBlocks` property, when set to `true` (defaults to `false`), ignores empty blocks containing comments, e.g.: + + ```apex + try { + doSomethingThatThrowsAnExpectedException(); + System.assert(false, 'Expected to catch an exception.'); + } catch (Exception e) { + // Expected + } + ``` + + The `allowExceptionNameRegex` property is a regular expression for exception variable names for which empty catch blocks should be ignored by this rule. For example, using the default property value of `^(ignored|expected)$`, the following empty catch blocks will not be reported: + + ```apex + try { + doSomethingThatThrowsAnExpectedException(); + System.assert(false, 'Expected to catch an exception.'); + } catch (IllegalStateException ignored) { + } catch (NumberFormatException expected) { + } + ``` + +* The Apex rule [`OneDeclarationPerLine`](https://pmd.github.io/pmd-6.41.0-SNAPSHOT/pmd_rules_apex_codestyle.html#onedeclarationperline) has a new property `reportInForLoopInitializer`: + If set to `false` (default is `true` if unspecified) doesn't report an issue for multiple declarations in + a `for` loop's initializer section. This is support the common idiom of one declaration for the loop variable + and another for the loop bounds condition, e.g., + + ```apex + for (Integer i = 0, numIterations = computeNumIterations(); i < numIterations; i++) { + } + ``` + +* The Java rule [`ClassNamingConventions`](https://pmd.github.io/pmd-6.41.0-SNAPSHOT/pmd_rules_java_codestyle.html#classnamingconventions) uses a different default value of the + property `utilityClassPattern`: This rule was detecting utility classes by default since PMD 6.3.0 + and enforcing the naming convention that utility classes has to be suffixed with Util or Helper or Constants. + However this turned out to be not so useful as a default configuration, as there is no standard + naming convention for utility classes. + + With PMD 6.40.0, the default value of this property has been changed to `[A-Z][a-zA-Z0-9]*` + (Pascal case), effectively disabling the special handling of utility classes. This is the same default + pattern used for concrete classes. + + This means, that the feature to enforce a naming convention for utility classes is now a opt-in + feature and can be enabled on demand. + + To use the old behaviour, the property needs to be configured as follows: + + ```xml + + + + + + ``` + + +### Fixed Issues + +* apex + * [#1089](https://github.com/pmd/pmd/issues/1089): \[apex] ApexUnitTestClassShouldHaveAsserts: Test asserts in other methods not detected + * [#1090](https://github.com/pmd/pmd/issues/1090): \[apex] ApexCRUDViolation: checks not detected if done in another method + * [#3532](https://github.com/pmd/pmd/issues/3532): \[apex] Promote usage of consistent getDescribe() info + * [#3566](https://github.com/pmd/pmd/issues/3566): \[apex] ApexDoc rule should not require "@description" + * [#3568](https://github.com/pmd/pmd/issues/3568): \[apex] EmptyStatementBlock: should provide options to ignore empty private constructors and empty virtual methods + * [#3569](https://github.com/pmd/pmd/issues/3569): \[apex] EmptyCatchBlock: should provide an option to ignore empty catch blocks in test methods + * [#3570](https://github.com/pmd/pmd/issues/3570): \[apex] OneDeclarationPerLine: should provide an option to ignore multiple declarations in a for loop initializer + * [#3576](https://github.com/pmd/pmd/issues/3576): \[apex] ApexCRUDViolation should provide an option to specify additional patterns for methods that encapsulate authorization checks + * [#3579](https://github.com/pmd/pmd/issues/3579): \[apex] ApexCRUDViolation: false negative with undelete +* java-bestpractices + * [#3542](https://github.com/pmd/pmd/issues/3542): \[java] MissingOverride: False negative for enum method +* java-codestyle + * [#1595](https://github.com/pmd/pmd/issues/1595): \[java] Discuss default for utility classes in ClassNamingConventions + * [#3563](https://github.com/pmd/pmd/issues/3563): \[java] The ClassNamingConventionsRule false-positive's on the class name "Constants" +* java-errorprone + * [#3560](https://github.com/pmd/pmd/issues/3560): \[java] InvalidLogMessageFormat: False positive with message and exception in a block inside a lambda +* java-performance + * [#2364](https://github.com/pmd/pmd/issues/2364): \[java] AddEmptyString false positive in annotation value +* java-security + * [#3368](https://github.com/pmd/pmd/issues/3368): \[java] HardcodedCryptoKey false negative with variable assignments + +### API Changes + +#### Experimental APIs + +* The interface ASTCommentContainer has been added to the Apex AST. + It provides a way to check whether a node contains at least one comment. Currently this is only implemented for + ASTCatchBlockStatement and used by the rule + [`EmptyCatchBlock`](https://pmd.github.io/pmd-6.41.0-SNAPSHOT/pmd_rules_apex_errorprone.html#emptycatchblock). + This information is also available via XPath attribute `@ContainsComment`. + +### External Contributions + +* [#3538](https://github.com/pmd/pmd/pull/3538): \[apex] New rule EagerlyLoadedDescribeSObjectResult - [Jonathan Wiesel](https://github.com/jonathanwiesel) +* [#3549](https://github.com/pmd/pmd/pull/3549): \[java] Ignore AddEmptyString rule in annotations - [Stanislav Myachenkov](https://github.com/smyachenkov) +* [#3561](https://github.com/pmd/pmd/pull/3561): \[java] InvalidLogMessageFormat: False positive with message and exception in a block inside a lambda - [Nicolas Filotto](https://github.com/essobedo) +* [#3565](https://github.com/pmd/pmd/pull/3565): \[doc] Fix resource leak due to Files.walk - [lujiefsi](https://github.com/lujiefsi) +* [#3571](https://github.com/pmd/pmd/pull/3571): \[apex] Fix for #1089 - Added new configuration property additionalAssertMethodPattern to ApexUnitTestClassShouldHaveAssertsRule - [Scott Wells](https://github.com/SCWells72) +* [#3572](https://github.com/pmd/pmd/pull/3572): \[apex] Fix for #3566 - Added new configuration property reportMissingDescription to ApexDocRule - [Scott Wells](https://github.com/SCWells72) +* [#3573](https://github.com/pmd/pmd/pull/3573): \[apex] Fix for #3568 - Added new configuration properties reportEmptyPrivateNoArgConstructor and reportEmptyVirtualMethod to EmptyStatementBlock - [Scott Wells](https://github.com/SCWells72) +* [#3574](https://github.com/pmd/pmd/pull/3574): \[apex] Fix for #3569 - Added new configuration properties allowCommentedBlocks and allowExceptionNameRegex to EmptyCatchBlock - [Scott Wells](https://github.com/SCWells72) +* [#3575](https://github.com/pmd/pmd/pull/3575): \[apex] Fix for #3570 - Added new configuration property reportInForLoopInitializer to OneDeclarationPerLine - [Scott Wells](https://github.com/SCWells72) +* [#3577](https://github.com/pmd/pmd/pull/3577): \[apex] Fix for #3576 - Added new configuration properties \*AuthMethodPattern and \*AuthMethodTypeParamIndex to ApexCRUDViolation rule - [Scott Wells](https://github.com/SCWells72) +* [#3578](https://github.com/pmd/pmd/pull/3578): \[apex] ApexCRUDViolation: Documentation changes for #3576 - [Scott Wells](https://github.com/SCWells72) +* [#3580](https://github.com/pmd/pmd/pull/3580): \[doc] Release notes updates for the changes in issue #3569 - [Scott Wells](https://github.com/SCWells72) +* [#3581](https://github.com/pmd/pmd/pull/3581): \[apex] #3569 - Requested changes for code review feedback - [Scott Wells](https://github.com/SCWells72) + +### Stats +* 72 commits +* 37 closed tickets & PRs +* Days since last release: 34 + +## 25-September-2021 - 6.39.0 + +The PMD team is pleased to announce PMD 6.39.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [All Contributors](#all-contributors) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +#### All Contributors + +PMD follows the [All Contributors](https://allcontributors.org/) specification. +Contributions of any kind welcome! + +See [credits](https://pmd.github.io/latest/pmd_projectdocs_credits.html) for our complete contributors list. + +### Fixed Issues + +* core + * [#3499](https://github.com/pmd/pmd/pull/3499): \[core] Fix XPath rulechain with combined node tests +* java-errorprone + * [#3493](https://github.com/pmd/pmd/pull/3493): \[java] AvoidAccessibilityAlteration: add tests and fix rule +* javascript + * [#3516](https://github.com/pmd/pmd/pull/3516): \[javascript] NPE while creating rule violation when specifying explicit line numbers +* plsql + * [#3487](https://github.com/pmd/pmd/issues/3487): \[plsql] Parsing exception OPEN ref_cursor_name FOR statement + * [#3515](https://github.com/pmd/pmd/issues/3515): \[plsql] Parsing exception SELECT...INTO on Associative Arrays Types + +### API Changes + +No changes. + +### External Contributions + +* [#3516](https://github.com/pmd/pmd/pull/3516): \[javascript] NPE while creating rule violation when specifying explicit line numbers - [Kevin Guerra](https://github.com/kevingnet) + +### Stats +* 37 commits +* 10 closed tickets & PRs +* Days since last release: 27 + +## 28-August-2021 - 6.38.0 + +The PMD team is pleased to announce PMD 6.38.0. + +This is a minor release. + +### Table Of Contents + +* [Fixed Issues](#fixed-issues) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### Fixed Issues + +* apex + * [#3462](https://github.com/pmd/pmd/issues/3462): \[apex] SOQL performed in a for-each loop doesn't trigger ApexCRUDViolationRule + * [#3484](https://github.com/pmd/pmd/issues/3484): \[apex] ApexCRUDViolationRule maintains state across files +* core + * [#3446](https://github.com/pmd/pmd/issues/3446): \[core] Allow XPath rules to access the current file name +* java-bestpractices + * [#3403](https://github.com/pmd/pmd/issues/3403): \[java] MethodNamingConventions junit5TestPattern does not detect parameterized tests + +### External Contributions + +* [#3445](https://github.com/pmd/pmd/pull/3445): \[java] Fix #3403 about MethodNamingConventions and JUnit5 parameterized tests - [Cyril Sicard](https://github.com/CyrilSicard) +* [#3470](https://github.com/pmd/pmd/pull/3470): \[apex] Fix ApexCRUDViolationRule - add super call - [Josh Feingold](https://github.com/jfeingold35) + +### Stats +* 32 commits +* 8 closed tickets & PRs +* Days since last release: 27 + +## 31-July-2021 - 6.37.0 + +The PMD team is pleased to announce PMD 6.37.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [Java 17 Support](#java-17-support) + * [Updated PMD Designer](#updated-pmd-designer) + * [New rules](#new-rules) + * [Renamed rules](#renamed-rules) + * [Deprecated rules](#deprecated-rules) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) + * [PMD CLI](#pmd-cli) + * [Experimental APIs](#experimental-apis) + * [Internal API](#internal-api) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +#### Java 17 Support + +This release of PMD brings support for Java 17. PMD supports [JEP 409: Sealed Classes](https://openjdk.java.net/jeps/409) +which has been promoted to be a standard language feature of Java 17. + +PMD also supports [JEP 406: Pattern Matching for switch (Preview)](https://openjdk.java.net/jeps/406) as a preview +language feature. In order to analyze a project with PMD that uses these language features, you'll need to enable +it via the environment variable `PMD_JAVA_OPTS` and select the new language version `17-preview`: + + export PMD_JAVA_OPTS=--enable-preview + ./run.sh pmd -language java -version 17-preview ... + +Note: Support for Java 15 preview language features have been removed. The version "15-preview" is no longer available. + +#### Updated PMD Designer + +This PMD release ships a new version of the pmd-designer. +For the changes, see [PMD Designer Changelog](https://github.com/pmd/pmd-designer/releases/tag/6.37.0). + +#### New rules + +This release ships with 3 new Java rules. + +* [`PrimitiveWrapperInstantiation`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_bestpractices.html#primitivewrapperinstantiation) reports usages of primitive wrapper + constructors. They are deprecated since Java 9 and should not be used. + +```xml + +``` + + The rule is part of the quickstart.xml ruleset. + +* [`SimplifiableTestAssertion`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_bestpractices.html#simplifiabletestassertion) suggests rewriting + some test assertions to be more readable. + +```xml + +``` + + The rule is part of the quickstart.xml ruleset. + +* [`ReturnEmptyCollectionRatherThanNull`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_errorprone.html#returnemptycollectionratherthannull) suggests returning empty collections / arrays + instead of null. + +```xml + +``` + + The rule is part of the quickstart.xml ruleset. + +#### Renamed rules + +* The Java rule [`MissingBreakInSwitch`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_errorprone.html#missingbreakinswitch) has been renamed to + [`ImplicitSwitchFallThrough`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_errorprone.html#implicitswitchfallthrough) (category error prone) to better reflect the rule's + purpose: The rule finds implicit fall-through cases in switch statements, which are most + likely unexpected. The old rule name described only one way how to avoid a fall-through, + namely using `break` but `continue`, `throw` and `return` avoid a fall-through + as well. This enables us to improve this rule in the future. + +#### Deprecated rules + +* The following Java rules are deprecated and removed from the quickstart ruleset, + as the new rule [`SimplifiableTestAssertion`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_bestpractices.html#simplifiabletestassertion) merges + their functionality: + * [`UseAssertEqualsInsteadOfAssertTrue`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_bestpractices.html#useassertequalsinsteadofasserttrue) + * [`UseAssertNullInsteadOfAssertTrue`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_bestpractices.html#useassertnullinsteadofasserttrue) + * [`UseAssertSameInsteadOfAssertTrue`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_bestpractices.html#useassertsameinsteadofasserttrue) + * [`UseAssertTrueInsteadOfAssertEquals`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_bestpractices.html#useasserttrueinsteadofassertequals) + * [`SimplifyBooleanAssertion`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_design.html#simplifybooleanassertion) + +* The Java rule [`ReturnEmptyArrayRatherThanNull`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_errorprone.html#returnemptyarrayratherthannull) is deprecated and removed from + the quickstart ruleset, as the new rule [`ReturnEmptyCollectionRatherThanNull`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_errorprone.html#returnemptycollectionratherthannull) + supersedes it. + +* The following Java rules are deprecated and removed from the quickstart ruleset, + as the new rule [`PrimitiveWrapperInstantiation`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_bestpractices.html#primitivewrapperinstantiation) merges + their functionality: + * [`BooleanInstantiation`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_performance.html#booleaninstantiation) + * [`ByteInstantiation`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_performance.html#byteinstantiation) + * [`IntegerInstantiation`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_performance.html#integerinstantiation) + * [`LongInstantiation`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_performance.html#longinstantiation) + * [`ShortInstantiation`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_performance.html#shortinstantiation) + +* The Java rule [`UnnecessaryWrapperObjectCreation`](https://pmd.github.io/pmd-6.37.0/pmd_rules_java_performance.html#unnecessarywrapperobjectcreation) is deprecated + with no planned replacement before PMD 7. In it's current state, the rule is not useful + as it finds only contrived cases of creating a primitive wrapper and unboxing it explicitly + in the same expression. In PMD 7 this and more cases will be covered by a + new rule `UnnecessaryBoxing`. + +### Fixed Issues + +* apex + * [#3201](https://github.com/pmd/pmd/issues/3201): \[apex] ApexCRUDViolation doesn't report Database class DMLs, inline no-arg object instantiations and inline list initialization + * [#3329](https://github.com/pmd/pmd/issues/3329): \[apex] ApexCRUDViolation doesn't report SOQL for loops +* core + * [#1603](https://github.com/pmd/pmd/issues/1603): \[core] Language version comparison + * [#2133](https://github.com/pmd/pmd/issues/2133): \[xml] Allow to check Salesforce XML Metadata using XPath rules + * [#3377](https://github.com/pmd/pmd/issues/3377): \[core] NPE when specifying report file in current directory in PMD CLI + * [#3387](https://github.com/pmd/pmd/issues/3387): \[core] CPD should avoid unnecessary copies when running with --skip-lexical-errors +* java-bestpractices + * [#2908](https://github.com/pmd/pmd/issues/2908): \[java] Merge Junit assertion simplification rules + * [#3235](https://github.com/pmd/pmd/issues/3235): \[java] UseTryWithResources false positive when closeable is provided as a method argument or class field +* java-errorprone + * [#3361](https://github.com/pmd/pmd/issues/3361): \[java] Rename rule MissingBreakInSwitch to ImplicitSwitchFallThrough + * [#3382](https://github.com/pmd/pmd/pull/3382): \[java] New rule ReturnEmptyCollectionRatherThanNull +* java-performance + * [#3420](https://github.com/pmd/pmd/issues/3420): \[java] NPE in `InefficientStringBuffering` with Records + +### API Changes + +#### PMD CLI + +* PMD has a new CLI option `-force-language`. With that a language can be forced to be used for all input files, + irrespective of filenames. When using this option, the automatic language selection by extension is disabled + and all files are tried to be parsed with the given language. Parsing errors are ignored and unparsable files + are skipped. + + This option allows to use the xml language for files, that don't use xml as extension. + See also the examples on [PMD CLI reference](pmd_userdocs_cli_reference.html#analyze-other-xml-formats). + +#### Experimental APIs + +* The AST types and APIs around Sealed Classes are not experimental anymore: + * ASTClassOrInterfaceDeclaration#isSealed, + ASTClassOrInterfaceDeclaration#isNonSealed, + ASTClassOrInterfaceDeclaration#getPermittedSubclasses + * ASTPermitsList + +#### Internal API + +Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. +You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. + +* The inner class net.sourceforge.pmd.cpd.TokenEntry.State is considered to be internal API. + It will probably be moved away with PMD 7. + +### External Contributions + +* [#3367](https://github.com/pmd/pmd/pull/3367): \[apex] Check SOQL CRUD on for loops - [Jonathan Wiesel](https://github.com/jonathanwiesel) +* [#3373](https://github.com/pmd/pmd/pull/3373): \[apex] Add ApexCRUDViolation support for database class, inline no-arg object construction DML and inline list initialization DML - [Jonathan Wiesel](https://github.com/jonathanwiesel) +* [#3385](https://github.com/pmd/pmd/pull/3385): \[core] CPD: Optimize --skip-lexical-errors option - [Woongsik Choi](https://github.com/woongsikchoi) +* [#3388](https://github.com/pmd/pmd/pull/3388): \[doc] Add Code Inspector in the list of tools - [Julien Delange](https://github.com/juli1) +* [#3417](https://github.com/pmd/pmd/pull/3417): \[core] Support forcing a specific language from the command-line - [Aidan Harding](https://github.com/aidan-harding) + +### Stats +* 82 commits +* 29 closed tickets & PRs +* Days since last release: 35 + ## 26-June-2021 - 6.36.0 The PMD team is pleased to announce PMD 6.36.0. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..f93d41f574 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1210 @@ +{ + "name": "pmd", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "all-contributors-cli": "^6.20.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz", + "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/all-contributors-cli": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/all-contributors-cli/-/all-contributors-cli-6.20.0.tgz", + "integrity": "sha512-trEQlL1s1u8FSWSwY2w9uL4GCG7Fo9HIW5rm5LtlE0SQHSolfXQBzJib07Qes5j52/t72wjuE6sEKkuRrwiuuQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.7.6", + "async": "^3.0.1", + "chalk": "^4.0.0", + "didyoumean": "^1.2.1", + "inquirer": "^7.0.4", + "json-fixer": "^1.5.1", + "lodash": "^4.11.2", + "node-fetch": "^2.6.0", + "pify": "^5.0.0", + "yargs": "^15.0.1" + }, + "bin": { + "all-contributors": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/async": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==", + "dev": true + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/json-fixer": { + "version": "1.6.12", + "resolved": "https://registry.npmjs.org/json-fixer/-/json-fixer-1.6.12.tgz", + "integrity": "sha512-BGO9HExf0ZUVYvuWsps71Re513Ss0il1Wp7wYWkir2NthzincvNJEUu82KagEfAkGdjOMsypj3t2JB7drBKWnA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.14.6", + "chalk": "^4.1.1", + "pegjs": "^0.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pegjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=", + "dev": true, + "bin": { + "pegjs": "bin/pegjs" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@babel/runtime": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz", + "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "all-contributors-cli": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/all-contributors-cli/-/all-contributors-cli-6.20.0.tgz", + "integrity": "sha512-trEQlL1s1u8FSWSwY2w9uL4GCG7Fo9HIW5rm5LtlE0SQHSolfXQBzJib07Qes5j52/t72wjuE6sEKkuRrwiuuQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.6", + "async": "^3.0.1", + "chalk": "^4.0.0", + "didyoumean": "^1.2.1", + "inquirer": "^7.0.4", + "json-fixer": "^1.5.1", + "lodash": "^4.11.2", + "node-fetch": "^2.6.0", + "pify": "^5.0.0", + "yargs": "^15.0.1" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "async": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "json-fixer": { + "version": "1.6.12", + "resolved": "https://registry.npmjs.org/json-fixer/-/json-fixer-1.6.12.tgz", + "integrity": "sha512-BGO9HExf0ZUVYvuWsps71Re513Ss0il1Wp7wYWkir2NthzincvNJEUu82KagEfAkGdjOMsypj3t2JB7drBKWnA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.14.6", + "chalk": "^4.1.1", + "pegjs": "^0.10.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pegjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=", + "dev": true + }, + "pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000..02062c9a7f --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "all-contributors-cli": "^6.20.0" + } +} diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml index 9c2da9d32c..2c15c20851 100644 --- a/pmd-apex-jorje/pom.xml +++ b/pmd-apex-jorje/pom.xml @@ -13,7 +13,7 @@ - 2020-09-10-5a5192 + 2021-10-08-631b8c diff --git a/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2020-09-10-5a5192/apex-jorje-lsp-minimized-2020-09-10-5a5192.jar b/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2021-10-08-631b8c/apex-jorje-lsp-minimized-2021-10-08-631b8c.jar similarity index 79% rename from pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2020-09-10-5a5192/apex-jorje-lsp-minimized-2020-09-10-5a5192.jar rename to pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2021-10-08-631b8c/apex-jorje-lsp-minimized-2021-10-08-631b8c.jar index 4546f33cc9..7dbe44ed3a 100644 Binary files a/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2020-09-10-5a5192/apex-jorje-lsp-minimized-2020-09-10-5a5192.jar and b/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2021-10-08-631b8c/apex-jorje-lsp-minimized-2021-10-08-631b8c.jar differ diff --git a/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2020-09-10-5a5192/apex-jorje-lsp-minimized-2020-09-10-5a5192.pom b/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2021-10-08-631b8c/apex-jorje-lsp-minimized-2021-10-08-631b8c.pom similarity index 91% rename from pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2020-09-10-5a5192/apex-jorje-lsp-minimized-2020-09-10-5a5192.pom rename to pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2021-10-08-631b8c/apex-jorje-lsp-minimized-2021-10-08-631b8c.pom index 3c74212901..25388ebc7a 100644 --- a/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2020-09-10-5a5192/apex-jorje-lsp-minimized-2020-09-10-5a5192.pom +++ b/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/2021-10-08-631b8c/apex-jorje-lsp-minimized-2021-10-08-631b8c.pom @@ -4,6 +4,6 @@ 4.0.0 apex apex-jorje-lsp-minimized - 2020-09-10-5a5192 + 2021-10-08-631b8c POM was created from install:install-file diff --git a/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/maven-metadata-local.xml b/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/maven-metadata-local.xml index d3df37e6e4..979af0c533 100644 --- a/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/maven-metadata-local.xml +++ b/pmd-apex-jorje/repo/apex/apex-jorje-lsp-minimized/maven-metadata-local.xml @@ -3,10 +3,10 @@ apex apex-jorje-lsp-minimized - 2020-09-10-5a5192 + 2021-10-08-631b8c - 2020-09-10-5a5192 + 2021-10-08-631b8c - 20201022163714 + 20211014081008 diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCatchBlockStatement.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCatchBlockStatement.java index f880d83f3f..6bf2d752ea 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCatchBlockStatement.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCatchBlockStatement.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.apex.ast; import apex.jorje.semantic.ast.statement.CatchBlockStatement; -public final class ASTCatchBlockStatement extends AbstractApexNode { +public final class ASTCatchBlockStatement extends AbstractApexCommentContainerNode { ASTCatchBlockStatement(CatchBlockStatement catchBlockStatement) { super(catchBlockStatement); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCommentContainer.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCommentContainer.java new file mode 100644 index 0000000000..53827efa2c --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCommentContainer.java @@ -0,0 +1,20 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import net.sourceforge.pmd.annotation.Experimental; + +import apex.jorje.semantic.ast.AstNode; + +/** + * Interface for nodes that can contain comments. Because comments are for the most part lost, the tree builder only + * captures whether the node did contain comments of any sort in the source code and not the actual contents of those + * comments. This is useful for rules which need to know whether a node did contain comments. + */ +@Experimental +public interface ASTCommentContainer extends ApexNode { + + boolean getContainsComment(); +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTLiteralExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTLiteralExpression.java index 7ee1627c53..59f92b4c6d 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTLiteralExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTLiteralExpression.java @@ -4,9 +4,10 @@ package net.sourceforge.pmd.lang.apex.ast; -import java.lang.reflect.Field; import java.util.Optional; +import org.apache.commons.lang3.reflect.FieldUtils; + import apex.jorje.data.Identifier; import apex.jorje.data.ast.LiteralType; import apex.jorje.semantic.ast.expression.LiteralExpression; @@ -69,29 +70,21 @@ public final class ASTLiteralExpression extends AbstractApexNode parameter = parent.node.getParameters().stream().filter(p -> { - try { - return this.node.equals(exprField.get(p)); - } catch (IllegalArgumentException | IllegalAccessException e) { - return false; - } - }).findFirst(); + Optional parameter = parent.node.getParameters().stream().filter(p -> { + try { + return this.node.equals(FieldUtils.readDeclaredField(p, "expression", true)); + } catch (IllegalArgumentException | ReflectiveOperationException e) { + return false; + } + }).findFirst(); - Field nameField = NameValueParameter.class.getDeclaredField("name"); - nameField.setAccessible(true); - return parameter.map(p -> { - try { - return (Identifier) nameField.get(p); - } catch (IllegalArgumentException | IllegalAccessException e) { - return null; - } - }).map(Identifier::getValue).orElse(null); - } catch (NoSuchFieldException | SecurityException e1) { - return null; - } + return parameter.map(p -> { + try { + return (Identifier) FieldUtils.readDeclaredField(p, "name", true); + } catch (IllegalArgumentException | ReflectiveOperationException e) { + return null; + } + }).map(Identifier::getValue).orElse(null); } return null; } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTModifierNode.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTModifierNode.java index 01a9065184..00d5ebb292 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTModifierNode.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTModifierNode.java @@ -102,4 +102,8 @@ public final class ASTModifierNode extends AbstractApexNode implem public boolean isOverride() { return node.getModifiers().has(ModifierTypeInfos.OVERRIDE); } + + public boolean isVirtual() { + return node.getModifiers().has(ModifierTypeInfos.VIRTUAL); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTTypeWhenBlock.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTTypeWhenBlock.java index 89726babb0..56dcbc49fc 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTTypeWhenBlock.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTTypeWhenBlock.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.apex.ast; -import java.lang.reflect.Field; +import org.apache.commons.lang3.reflect.FieldUtils; import apex.jorje.semantic.ast.statement.TypeWhenBlock; @@ -22,10 +22,8 @@ public final class ASTTypeWhenBlock extends AbstractApexNode { public String getName() { // unfortunately the name is not exposed... try { - Field nameField = TypeWhenBlock.class.getDeclaredField("name"); - nameField.setAccessible(true); - return String.valueOf(nameField.get(node)); - } catch (SecurityException | ReflectiveOperationException e) { + return String.valueOf(FieldUtils.readDeclaredField(node, "name", true)); + } catch (IllegalArgumentException | ReflectiveOperationException e) { return null; } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClass.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClass.java index c8d28d2af0..ac61c4a139 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClass.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClass.java @@ -13,12 +13,10 @@ import apex.jorje.semantic.ast.compilation.UserClass; public final class ASTUserClass extends BaseApexClass implements ASTUserClassOrInterface { - ASTUserClass(UserClass userClass) { super(userClass); } - @Override protected R acceptApexVisitor(ApexVisitor visitor, P data) { return visitor.visit(this, data); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexCommentContainerNode.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexCommentContainerNode.java new file mode 100644 index 0000000000..412af06459 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexCommentContainerNode.java @@ -0,0 +1,30 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.AstNode; + +/** + * Abstract base class for nodes which can contain comments. + * + * @param the node type + */ +abstract class AbstractApexCommentContainerNode extends AbstractApexNode implements ASTCommentContainer { + + private boolean containsComment = false; + + protected AbstractApexCommentContainerNode(T node) { + super(node); + } + + void setContainsComment(boolean containsComment) { + this.containsComment = containsComment; + } + + @Override + public boolean getContainsComment() { + return containsComment; + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java index 6bc49b2b09..a99298cc5e 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java @@ -6,10 +6,13 @@ package net.sourceforge.pmd.lang.apex.ast; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.RandomAccess; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -128,7 +131,7 @@ final class ApexTreeBuilder extends AstVisitor { private static final Pattern COMMENT_PATTERN = // we only need to check for \n as the input is normalized - Pattern.compile("/\\*\\*([^*]++|\\*(?!/))*+\\*/|//[^\n]++\n"); + Pattern.compile("/\\*([^*]++|\\*(?!/))*+\\*/|//[^\n]++\n"); private static final Map, Constructor>> NODE_TYPE_TO_NODE_ADAPTER_TYPE = new HashMap<>(); @@ -251,16 +254,12 @@ final class ApexTreeBuilder extends AstVisitor { private final TextDocument sourceCode; private final ParserTask task; - private final List apexDocTokenLocations; - private final Map suppressMap; + private final CommentInformation commentInfo; ApexTreeBuilder(ParserTask task) { this.sourceCode = task.getTextDocument(); this.task = task; - - CommentInformation commentInformation = extractInformationFromComments(sourceCode, task.getCommentMarker()); - apexDocTokenLocations = commentInformation.docTokenLocations; - suppressMap = commentInformation.suppressMap; + commentInfo = extractInformationFromComments(sourceCode, task.getCommentMarker()); } static AbstractApexNode createNodeAdapter(T node) { @@ -284,7 +283,7 @@ final class ApexTreeBuilder extends AstVisitor { ASTApexFile buildTree(Compilation astNode, ApexMultifileAnalysis analysisHandler) { assert nodes.isEmpty() : "stack should be empty"; - ASTApexFile root = new ASTApexFile(task, astNode, suppressMap, analysisHandler); + ASTApexFile root = new ASTApexFile(task, astNode, commentInfo.suppressMap, analysisHandler); nodes.push(root); parents.push(astNode); @@ -312,6 +311,20 @@ final class ApexTreeBuilder extends AstVisitor { astNode.traverse(this, scope); nodes.pop(); parents.pop(); + + + if (nodes.isEmpty()) { + // add the comments only at the end of the processing as the last step + addFormalComments(); + } + + // If appropriate, determine whether this node contains comments or not + if (node instanceof AbstractApexCommentContainerNode) { + AbstractApexCommentContainerNode commentContainer = (AbstractApexCommentContainerNode) node; + if (containsComments(commentContainer)) { + commentContainer.setContainsComment(true); + } + } } private void closeTree(AbstractApexNode node) { @@ -321,8 +334,30 @@ final class ApexTreeBuilder extends AstVisitor { } } + private boolean containsComments(ASTCommentContainer commentContainer) { + Location loc = commentContainer.getNode().getLoc(); + if (!Locations.isReal(loc)) { + // Synthetic nodes don't have a location and can't have comments + return false; + } + + List allComments = commentInfo.allCommentTokens; + // find the first comment after the start of the container node + int index = Collections.binarySearch(commentInfo.allCommentTokensByStartIndex, loc.getStartIndex()); + + // no exact hit found - this is expected: there is no comment token starting at the very same index as the node + assert index < 0 : "comment token is at the same position as non-comment token"; + // extract "insertion point" + index = ~index; + + // now check whether the next comment after the node is still inside the node + return index >= 0 && index < allComments.size() + && loc.getStartIndex() < allComments.get(index).region.getStartOffset() + && loc.getEndIndex() >= allComments.get(index).region.getEndOffset(); + } + private void addFormalComments() { - for (ApexDocTokenLocation tokenLocation : apexDocTokenLocations) { + for (ApexDocTokenLocation tokenLocation : commentInfo.docTokenLocations) { AbstractApexNode parent = tokenLocation.nearestNode; if (parent != null) { parent.insertChild(new ASTFormalComment(tokenLocation.region, tokenLocation.image), 0); @@ -342,6 +377,7 @@ final class ApexTreeBuilder extends AstVisitor { * the nodes appearing later in the source might be visiting first. * The correct node will then be visited afterwards, and since the distance * to the comment is smaller, it overrides the remembered node. + * * @param jorjeNode the original node * @param node the potential parent node, to which the comment could belong */ @@ -354,7 +390,7 @@ final class ApexTreeBuilder extends AstVisitor { } // find the token, that appears as close as possible before the node TextRegion nodeRegion = node.getRegion(); - for (ApexDocTokenLocation comment : apexDocTokenLocations) { + for (ApexDocTokenLocation comment : commentInfo.docTokenLocations) { if (comment.region.compareTo(nodeRegion) > 0) { // this and all remaining tokens are after the node // so no need to check the remaining tokens. @@ -373,19 +409,29 @@ final class ApexTreeBuilder extends AstVisitor { Chars text = source.getText(); boolean checkForCommentSuppression = suppressMarker != null; - List tokenLocations = new LinkedList<>(); + ArrayList allCommentTokens = new ArrayList<>(); + List tokenLocations = new ArrayList<>(); Map suppressMap = new HashMap<>(); + Matcher matcher = COMMENT_PATTERN.matcher(text); while (matcher.find()) { int startIdx = matcher.start(); int endIdx = matcher.end(); Chars commentText = text.subSequence(startIdx, endIdx); + TextRegion commentRegion = TextRegion.fromBothOffsets(startIdx, endIdx); + final TokenLocation tok; if (commentText.startsWith("/**")) { - TextRegion commentRegion = TextRegion.fromBothOffsets(startIdx, endIdx); - tokenLocations.add(new ApexDocTokenLocation(commentRegion, commentText)); - } else if (checkForCommentSuppression && commentText.startsWith("//")) { + ApexDocTokenLocation doctok = new ApexDocTokenLocation(commentRegion, commentText); + tokenLocations.add(doctok); + tok = doctok; + } else { + tok = new TokenLocation(commentRegion); + } + allCommentTokens.add(tok); + + if (checkForCommentSuppression && commentText.startsWith("//")) { Chars trimmed = commentText.subSequence("//".length(), commentText.length()).trimStart(); if (trimmed.startsWith(suppressMarker)) { Chars userMessage = trimmed.subSequence(suppressMarker.length(), trimmed.length()).trim(); @@ -393,30 +439,69 @@ final class ApexTreeBuilder extends AstVisitor { } } } - - return new CommentInformation(suppressMap, tokenLocations); + return new CommentInformation(suppressMap, allCommentTokens, tokenLocations); } private static class CommentInformation { - Map suppressMap; - List docTokenLocations; - CommentInformation(Map suppressMap, List docTokenLocations) { + final Map suppressMap; + final List allCommentTokens; + final TokenListByStartIndex allCommentTokensByStartIndex; + final List docTokenLocations; + + & RandomAccess> + CommentInformation(Map suppressMap, T allCommentTokens, List docTokenLocations) { this.suppressMap = suppressMap; + this.allCommentTokens = allCommentTokens; this.docTokenLocations = docTokenLocations; + this.allCommentTokensByStartIndex = new TokenListByStartIndex(allCommentTokens); } } - private static class ApexDocTokenLocation { + /** + * List that maps comment tokens to their start index without copy. + * This is used to implement a "binary search by key" routine which unfortunately isn't in the stdlib. + * + *

+ * Note that the provided token list must implement {@link RandomAccess}. + */ + private static final class TokenListByStartIndex extends AbstractList implements RandomAccess { + + private final List tokens; + + & RandomAccess> TokenListByStartIndex(T tokens) { + this.tokens = tokens; + } + + @Override + public Integer get(int index) { + return tokens.get(index).region.getStartOffset(); + } + + @Override + public int size() { + return tokens.size(); + } + } + + private static class TokenLocation { + + final TextRegion region; + + TokenLocation(TextRegion region) { + this.region = region; + } + } + + private static class ApexDocTokenLocation extends TokenLocation { - private final TextRegion region; private final Chars image; private AbstractApexNode nearestNode; private int nearestNodeDistance; ApexDocTokenLocation(TextRegion commentRegion, Chars image) { - this.region = commentRegion; + super(commentRegion); this.image = image; } } @@ -431,7 +516,7 @@ final class ApexTreeBuilder extends AstVisitor { } public Map getSuppressMap() { - return suppressMap; + return commentInfo.suppressMap; } @Override diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java index a9ca14098d..0e242fca56 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java @@ -93,5 +93,4 @@ class CompilerService { return new CompilationInput(sourceFiles, symbolProvider, accessEvaluator, queryValidator, null, NoopCompilerProgressCallback.get()); } - } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/EmptySymbolProvider.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/EmptySymbolProvider.java index 4b409f732c..17fcbcdb11 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/EmptySymbolProvider.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/EmptySymbolProvider.java @@ -81,4 +81,14 @@ final class EmptySymbolProvider implements SymbolProvider { public TypeInfo getAggregateResultType(TypeInfo arg0) { return null; } + + @Override + public boolean isDynamicTypeNamespace(String var1, String var2) { + return false; + } + + @Override + public boolean isDynamicTypeNamespace(String var1) { + return false; + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestAccessEvaluator.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestAccessEvaluator.java index 50c4176d8b..77ddb50a66 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestAccessEvaluator.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestAccessEvaluator.java @@ -358,4 +358,9 @@ class TestAccessEvaluator implements AccessEvaluator { public boolean isNamespaceGuardNamespace(Namespace arg0) { return false; } + + @Override + public boolean doesLightningWebComponentExist(String var1) { + return false; + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java index 0606311632..79e0e311e8 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -22,5 +22,4 @@ public abstract class AbstractApexRule extends AbstractRule public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); } - } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexUnitTestClassShouldHaveAssertsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexUnitTestClassShouldHaveAssertsRule.java index 76e6023fd6..084a25daf6 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexUnitTestClassShouldHaveAssertsRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/ApexUnitTestClassShouldHaveAssertsRule.java @@ -4,11 +4,17 @@ package net.sourceforge.pmd.lang.apex.rule.bestpractices; +import static net.sourceforge.pmd.properties.PropertyFactory.stringProperty; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.lang.apex.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTMethod; @@ -16,6 +22,7 @@ import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression; import net.sourceforge.pmd.lang.apex.ast.ASTStatement; import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.apex.rule.AbstractApexUnitTestRule; +import net.sourceforge.pmd.properties.PropertyDescriptor; /** * Apex unit tests should have System.assert methods in them @@ -30,6 +37,22 @@ public class ApexUnitTestClassShouldHaveAssertsRule extends AbstractApexUnitTest ASSERT_METHODS.add("system.assert"); ASSERT_METHODS.add("system.assertequals"); ASSERT_METHODS.add("system.assertnotequals"); + // Fully-qualified variants...rare but still valid/possible + ASSERT_METHODS.add("system.system.assert"); + ASSERT_METHODS.add("system.system.assertequals"); + ASSERT_METHODS.add("system.system.assertnotequals"); + } + + // Using a string property instead of a regex property to ensure that the compiled pattern can be case-insensitive + private static final PropertyDescriptor ADDITIONAL_ASSERT_METHOD_PATTERN_DESCRIPTOR = + stringProperty("additionalAssertMethodPattern") + .desc("A regular expression for one or more custom test assertion method patterns.").defaultValue("").build(); + + // A simple compiled pattern cache to ensure that we only ever try to compile the configured pattern once for a given run + private Optional compiledAdditionalAssertMethodPattern = null; + + public ApexUnitTestClassShouldHaveAssertsRule() { + definePropertyDescriptor(ADDITIONAL_ASSERT_METHOD_PATTERN_DESCRIPTOR); } @Override @@ -58,10 +81,42 @@ public class ApexUnitTestClassShouldHaveAssertsRule extends AbstractApexUnitTest } } + // If we didn't find assert method invocations the simple way and we have a configured pattern, try it + if (!isAssertFound) { + final String additionalAssertMethodPattern = getProperty(ADDITIONAL_ASSERT_METHOD_PATTERN_DESCRIPTOR); + final Pattern compiledPattern = getCompiledAdditionalAssertMethodPattern(additionalAssertMethodPattern); + if (compiledPattern != null) { + for (final ASTMethodCallExpression methodCallExpression : methodCalls) { + final String fullMethodName = methodCallExpression.getFullMethodName(); + if (compiledPattern.matcher(fullMethodName).matches()) { + isAssertFound = true; + break; + } + } + } + } + if (!isAssertFound) { addViolation(data, node); } return data; } + + private Pattern getCompiledAdditionalAssertMethodPattern(String additionalAssertMethodPattern) { + if (StringUtils.isNotBlank(additionalAssertMethodPattern)) { + // Check for presence first since we will cache a null value for patterns that don't compile + if (compiledAdditionalAssertMethodPattern == null) { + try { + compiledAdditionalAssertMethodPattern = Optional.of(Pattern.compile(additionalAssertMethodPattern, Pattern.CASE_INSENSITIVE)); + } catch (IllegalArgumentException e) { + // Cache a null compiled pattern so that we won't try to compile this one again during the run + compiledAdditionalAssertMethodPattern = Optional.ofNullable(null); + throw e; + } + } + } + + return compiledAdditionalAssertMethodPattern != null ? compiledAdditionalAssertMethodPattern.get() : null; + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/documentation/ApexDocRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/documentation/ApexDocRule.java index f59388a030..c96275052d 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/documentation/ApexDocRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/documentation/ApexDocRule.java @@ -48,9 +48,14 @@ public class ApexDocRule extends AbstractApexRule { booleanProperty("reportProtected") .desc("Report protected methods").defaultValue(false).build(); + private static final PropertyDescriptor REPORT_MISSING_DESCRIPTION_DESCRIPTOR = + booleanProperty("reportMissingDescription") + .desc("Report missing @description").defaultValue(true).build(); + public ApexDocRule() { definePropertyDescriptor(REPORT_PRIVATE_DESCRIPTOR); definePropertyDescriptor(REPORT_PROTECTED_DESCRIPTOR); + definePropertyDescriptor(REPORT_MISSING_DESCRIPTION_DESCRIPTOR); } @Override @@ -83,7 +88,7 @@ public class ApexDocRule extends AbstractApexRule { addViolationWithMessage(data, node, MISSING_COMMENT_MESSAGE); } } else { - if (!comment.hasDescription) { + if (getProperty(REPORT_MISSING_DESCRIPTION_DESCRIPTOR) && !comment.hasDescription) { addViolationWithMessage(data, node, MISSING_DESCRIPTION_MESSAGE); } @@ -117,7 +122,7 @@ public class ApexDocRule extends AbstractApexRule { addViolationWithMessage(data, node, MISSING_COMMENT_MESSAGE); } } else { - if (!comment.hasDescription) { + if (getProperty(REPORT_MISSING_DESCRIPTION_DESCRIPTOR) && !comment.hasDescription) { addViolationWithMessage(data, node, MISSING_DESCRIPTION_MESSAGE); } } @@ -132,7 +137,7 @@ public class ApexDocRule extends AbstractApexRule { addViolationWithMessage(data, node, MISSING_COMMENT_MESSAGE); } } else { - if (!comment.hasDescription) { + if (getProperty(REPORT_MISSING_DESCRIPTION_DESCRIPTOR) && !comment.hasDescription) { addViolationWithMessage(data, node, MISSING_DESCRIPTION_MESSAGE); } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/internal/Helper.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/internal/Helper.java index 4be3586849..e8fdc1641d 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/internal/Helper.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/internal/Helper.java @@ -20,6 +20,7 @@ import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression; import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode; import net.sourceforge.pmd.lang.apex.ast.ASTNewKeyValueObjectExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTNewObjectExpression; import net.sourceforge.pmd.lang.apex.ast.ASTParameter; import net.sourceforge.pmd.lang.apex.ast.ASTReferenceExpression; import net.sourceforge.pmd.lang.apex.ast.ASTSoqlExpression; @@ -164,6 +165,13 @@ public final class Helper { return sb.toString(); } + public static String getFQVariableName(final ASTNewObjectExpression variable) { + StringBuilder sb = new StringBuilder() + .append(variable.getDefiningType()).append(":") + .append(variable.getType()); + return sb.toString(); + } + public static boolean isSystemLevelClass(ASTUserClass node) { List interfaces = node.getInterfaceNames(); return interfaces.stream().anyMatch(Helper::isAllowed); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidDmlStatementsInLoopsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidDmlStatementsInLoopsRule.java index 8dfb04fed7..f98261517e 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidDmlStatementsInLoopsRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidDmlStatementsInLoopsRule.java @@ -17,6 +17,7 @@ import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpsertStatement; @Deprecated public class AvoidDmlStatementsInLoopsRule extends AbstractAvoidNodeInLoopsRule { + // CPD-OFF - the same visits are in the replacement rule OperationWithLimitsInLoopRule @Override public Object visit(ASTDmlDeleteStatement node, Object data) { return checkForViolation(node, data); @@ -46,4 +47,5 @@ public class AvoidDmlStatementsInLoopsRule extends AbstractAvoidNodeInLoopsRule public Object visit(ASTDmlUpsertStatement node, Object data) { return checkForViolation(node, data); } + // CPD-ON } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java index 0f4881bf95..7a06c93447 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java @@ -4,6 +4,9 @@ package net.sourceforge.pmd.lang.apex.rule.security; +import static net.sourceforge.pmd.properties.PropertyFactory.intProperty; +import static net.sourceforge.pmd.properties.PropertyFactory.stringProperty; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -18,20 +21,28 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; + +import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.apex.ast.ASTAssignmentExpression; import net.sourceforge.pmd.lang.apex.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlDeleteStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlInsertStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlMergeStatement; +import net.sourceforge.pmd.lang.apex.ast.ASTDmlUndeleteStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpdateStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpsertStatement; import net.sourceforge.pmd.lang.apex.ast.ASTField; import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclarationStatements; +import net.sourceforge.pmd.lang.apex.ast.ASTForEachStatement; import net.sourceforge.pmd.lang.apex.ast.ASTIfElseBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTMethod; import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression; import net.sourceforge.pmd.lang.apex.ast.ASTNewKeyValueObjectExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTNewListInitExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTNewListLiteralExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTNewObjectExpression; import net.sourceforge.pmd.lang.apex.ast.ASTProperty; import net.sourceforge.pmd.lang.apex.ast.ASTReferenceExpression; import net.sourceforge.pmd.lang.apex.ast.ASTReturnStatement; @@ -43,10 +54,10 @@ import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; import net.sourceforge.pmd.lang.apex.rule.internal.Helper; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.properties.PropertyDescriptor; import com.google.common.base.Objects; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ListMultimap; +import com.google.common.collect.HashMultimap; /** * Finding missed CRUD checks for SOQL and DML operations. @@ -60,6 +71,7 @@ public class ApexCRUDViolationRule extends AbstractApexRule { private static final String IS_CREATEABLE = "isCreateable"; private static final String IS_DELETABLE = "isDeletable"; + private static final String IS_UNDELETABLE = "isUndeletable"; private static final String IS_UPDATEABLE = "isUpdateable"; private static final String IS_MERGEABLE = "isMergeable"; private static final String IS_ACCESSIBLE = "isAccessible"; @@ -76,17 +88,84 @@ public class ApexCRUDViolationRule extends AbstractApexRule { "isAuthorizedToUpdate", }; private static final String[] ESAPI_ISAUTHORIZED_TO_DELETE = new String[] { "ESAPI", "accessController", "isAuthorizedToDelete", }; + // ESAPI doesn't provide support for undelete or merge private static final String[] RESERVED_KEYS_FLS = new String[] { "Schema", S_OBJECT_TYPE, }; private static final Pattern WITH_SECURITY_ENFORCED = Pattern.compile("(?is).*[^']\\s*WITH\\s+SECURITY_ENFORCED\\s*[^']*"); - private final Map varToTypeMapping = new HashMap<>(); - private final ListMultimap typeToDMLOperationMapping = ArrayListMultimap.create(); - private final Map checkedTypeToDMLOperationViaESAPI = new HashMap<>(); - private final Map classMethods = new WeakHashMap<>(); + // AuthMethodPattern config properties; these are string properties instead of regex properties to help + // ensure that the compiled patterns are case-insensitive vs. requiring the pattern author to use "(?i)" + private static final PropertyDescriptor CREATE_AUTH_METHOD_PATTERN_DESCRIPTOR = authMethodPatternProperty("create"); + private static final PropertyDescriptor READ_AUTH_METHOD_PATTERN_DESCRIPTOR = authMethodPatternProperty("read"); + private static final PropertyDescriptor UPDATE_AUTH_METHOD_PATTERN_DESCRIPTOR = authMethodPatternProperty("update"); + private static final PropertyDescriptor DELETE_AUTH_METHOD_PATTERN_DESCRIPTOR = authMethodPatternProperty("delete"); + private static final PropertyDescriptor UNDELETE_AUTH_METHOD_PATTERN_DESCRIPTOR = authMethodPatternProperty("undelete"); + private static final PropertyDescriptor MERGE_AUTH_METHOD_PATTERN_DESCRIPTOR = authMethodPatternProperty("merge"); + + // AuthMethodTypeParamIndex config properties + private static final PropertyDescriptor CREATE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR = authMethodTypeParamIndexProperty("create"); + private static final PropertyDescriptor READ_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR = authMethodTypeParamIndexProperty("read"); + private static final PropertyDescriptor UPDATE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR = authMethodTypeParamIndexProperty("update"); + private static final PropertyDescriptor DELETE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR = authMethodTypeParamIndexProperty("delete"); + private static final PropertyDescriptor UNDELETE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR = authMethodTypeParamIndexProperty("undelete"); + private static final PropertyDescriptor MERGE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR = authMethodTypeParamIndexProperty("merge"); + + // Auth method config property correlation information + private static final Map, PropertyDescriptor> AUTH_METHOD_TO_TYPE_PARAM_INDEX_MAP = new HashMap, PropertyDescriptor>() { + { + put(CREATE_AUTH_METHOD_PATTERN_DESCRIPTOR, CREATE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR); + put(READ_AUTH_METHOD_PATTERN_DESCRIPTOR, READ_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR); + put(UPDATE_AUTH_METHOD_PATTERN_DESCRIPTOR, UPDATE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR); + put(DELETE_AUTH_METHOD_PATTERN_DESCRIPTOR, DELETE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR); + put(UNDELETE_AUTH_METHOD_PATTERN_DESCRIPTOR, UNDELETE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR); + put(MERGE_AUTH_METHOD_PATTERN_DESCRIPTOR, MERGE_AUTH_METHOD_TYPE_PARAM_INDEX_DESCRIPTOR); + } + }; + private static final Map, String> AUTH_METHOD_TO_DML_OPERATION_MAP = new HashMap, String>() { + { + put(CREATE_AUTH_METHOD_PATTERN_DESCRIPTOR, IS_CREATEABLE); + put(READ_AUTH_METHOD_PATTERN_DESCRIPTOR, IS_ACCESSIBLE); + put(UPDATE_AUTH_METHOD_PATTERN_DESCRIPTOR, IS_UPDATEABLE); + put(DELETE_AUTH_METHOD_PATTERN_DESCRIPTOR, IS_DELETABLE); + put(UNDELETE_AUTH_METHOD_PATTERN_DESCRIPTOR, IS_UNDELETABLE); + put(MERGE_AUTH_METHOD_PATTERN_DESCRIPTOR, IS_MERGEABLE); + } + }; + + // Compiled pattern cache for configured method name patterns + private final Map compiledAuthMethodPatternCache = new HashMap<>(); + + private Map varToTypeMapping; + private HashMultimap typeToDMLOperationMapping; + private Map checkedTypeToDMLOperationViaESAPI; + private HashMultimap checkedTypeToDMLOperationsViaAuthPattern; + private Map classMethods; private String className; + public ApexCRUDViolationRule() { + // Register auth method config properties + for (Map.Entry, PropertyDescriptor> entry : AUTH_METHOD_TO_TYPE_PARAM_INDEX_MAP.entrySet()) { + PropertyDescriptor authMethodPatternDescriptor = entry.getKey(); + PropertyDescriptor authMethodTypeParamIndexDescriptor = entry.getValue(); + definePropertyDescriptor(authMethodPatternDescriptor); + definePropertyDescriptor(authMethodTypeParamIndexDescriptor); + } + } + + @Override + public void start(RuleContext ctx) { + // At the start of each rule execution, these member variables need to be fresh. So they're initialized in the + // .start() method instead of the constructor, since .start() is called before every execution. + varToTypeMapping = new HashMap<>(); + typeToDMLOperationMapping = HashMultimap.create(); + checkedTypeToDMLOperationViaESAPI = new HashMap<>(); + checkedTypeToDMLOperationsViaAuthPattern = HashMultimap.create(); + classMethods = new WeakHashMap<>(); + className = null; + super.start(ctx); + } + @Override public Object visit(ASTUserClass node, Object data) { if (Helper.isTestMethodOrClass(node) || Helper.isSystemLevelClass(node)) { @@ -107,7 +186,36 @@ public class ApexCRUDViolationRule extends AbstractApexRule { @Override public Object visit(ASTMethodCallExpression node, Object data) { - collectCRUDMethodLevelChecks(node); + if (Helper.isAnyDatabaseMethodCall(node)) { + + switch (node.getMethodName().toLowerCase(Locale.ROOT)) { + case "insert": + checkForCRUD(node, data, IS_CREATEABLE); + break; + case "update": + checkForCRUD(node, data, IS_UPDATEABLE); + break; + case "delete": + checkForCRUD(node, data, IS_DELETABLE); + break; + case "undelete": + checkForCRUD(node, data, IS_UNDELETABLE); + break; + case "upsert": + checkForCRUD(node, data, IS_CREATEABLE); + checkForCRUD(node, data, IS_UPDATEABLE); + break; + case "merge": + checkForCRUD(node, data, IS_MERGEABLE); + break; + default: + break; + } + + } else { + collectCRUDMethodLevelChecks(node); + } + return data; } @@ -123,6 +231,12 @@ public class ApexCRUDViolationRule extends AbstractApexRule { return data; } + @Override + public Object visit(ASTDmlUndeleteStatement node, Object data) { + checkForCRUD(node, data, IS_UNDELETABLE); + return data; + } + @Override public Object visit(ASTDmlUpdateStatement node, Object data) { checkForCRUD(node, data, IS_UPDATEABLE); @@ -203,6 +317,16 @@ public class ApexCRUDViolationRule extends AbstractApexRule { return data; } + @Override + public Object visit(final ASTForEachStatement node, Object data) { + final ASTSoqlExpression soql = node.getFirstChildOfType(ASTSoqlExpression.class); + if (soql != null) { + checkForAccessibility(soql, data); + } + + return super.visit(node, data); + } + private void addVariableToMapping(final String variableName, final String type) { switch (type.toLowerCase(Locale.ROOT)) { case "list": @@ -266,6 +390,8 @@ public class ApexCRUDViolationRule extends AbstractApexRule { extractObjectTypeFromESAPI(node, IS_DELETABLE); } + // ESAPI doesn't provide support for undelete or merge + // see if getDescribe() final ASTMethodCallExpression nestedMethodCall = ref .getFirstChildOfType(ASTMethodCallExpression.class); @@ -277,7 +403,11 @@ public class ApexCRUDViolationRule extends AbstractApexRule { } } } + } + // Check any configured authorization class library patterns + for (PropertyDescriptor authMethodPatternDescriptor : AUTH_METHOD_TO_TYPE_PARAM_INDEX_MAP.keySet()) { + extractObjectTypeFromConfiguredMethodPatternInvocation(node, authMethodPatternDescriptor); } } @@ -336,11 +466,8 @@ public class ApexCRUDViolationRule extends AbstractApexRule { return; } - final ASTNewKeyValueObjectExpression newObj = node.getFirstChildOfType(ASTNewKeyValueObjectExpression.class); - if (newObj != null) { - final String type = Helper.getFQVariableName(newObj); - validateCRUDCheckPresent(node, data, crudMethod, type); - } + checkInlineObject(node, data, crudMethod); + checkInlineNonArgsObject(node, data, crudMethod); final ASTVariableExpression variable = node.getFirstChildOfType(ASTVariableExpression.class); if (variable != null) { @@ -352,6 +479,36 @@ public class ApexCRUDViolationRule extends AbstractApexRule { validateCRUDCheckPresent(node, data, crudMethod, typeCheck.toString()); } } + + final ASTNewListLiteralExpression inlineListLiteral = node.getFirstChildOfType(ASTNewListLiteralExpression.class); + if (inlineListLiteral != null) { + checkInlineObject(inlineListLiteral, data, crudMethod); + checkInlineNonArgsObject(inlineListLiteral, data, crudMethod); + } + + final ASTNewListInitExpression inlineListInit = node.getFirstChildOfType(ASTNewListInitExpression.class); + if (inlineListInit != null) { + checkInlineObject(inlineListInit, data, crudMethod); + checkInlineNonArgsObject(inlineListInit, data, crudMethod); + } + } + + private void checkInlineObject(final ApexNode node, final Object data, final String crudMethod) { + + final ASTNewKeyValueObjectExpression newObj = node.getFirstChildOfType(ASTNewKeyValueObjectExpression.class); + if (newObj != null) { + final String type = Helper.getFQVariableName(newObj); + validateCRUDCheckPresent(node, data, crudMethod, type); + } + } + + private void checkInlineNonArgsObject(final ApexNode node, final Object data, final String crudMethod) { + + final ASTNewObjectExpression newEmptyObj = node.getFirstChildOfType(ASTNewObjectExpression.class); + if (newEmptyObj != null) { + final String type = Helper.getFQVariableName(newEmptyObj); + validateCRUDCheckPresent(node, data, crudMethod, type); + } } private Set getPreviousMethodCalls(final ApexNode self) { @@ -361,7 +518,7 @@ public class ApexCRUDViolationRule extends AbstractApexRule { final ASTBlockStatement blockStatement = outerMethod.getFirstChildOfType(ASTBlockStatement.class); recursivelyEvaluateCRUDMethodCalls(self, innerMethodCalls, blockStatement); - final List constructorMethods = findConstructorlMethods(); + final List constructorMethods = findConstructorMethods(); for (ASTMethod method : constructorMethods) { innerMethodCalls.addAll(method.findDescendantsOfType(ASTMethodCallExpression.class)); } @@ -410,12 +567,16 @@ public class ApexCRUDViolationRule extends AbstractApexRule { final ASTMethod methodBody = resolveMethodCalls(node); if (methodBody != null) { innerMethodCalls.addAll(methodBody.findDescendantsOfType(ASTMethodCallExpression.class)); + } else { + // If we couldn't resolve it locally, add any calls for configured authorization patterns + if (isAuthMethodInvocation(node)) { + innerMethodCalls.add(node); + } } - } } - private List findConstructorlMethods() { + private List findConstructorMethods() { final ArrayList ret = new ArrayList<>(); final Set constructors = classMethods.keySet().stream() .filter(p -> p.contains("") || p.contains("") @@ -435,7 +596,7 @@ public class ApexCRUDViolationRule extends AbstractApexRule { } private boolean isProperESAPICheckForDML(final String typeToCheck, final String dmlOperation) { - final boolean hasMapping = checkedTypeToDMLOperationViaESAPI.containsKey(typeToCheck.toString()); + final boolean hasMapping = checkedTypeToDMLOperationViaESAPI.containsKey(typeToCheck); if (hasMapping) { if (ANY.equals(dmlOperation)) { return true; @@ -465,21 +626,22 @@ public class ApexCRUDViolationRule extends AbstractApexRule { } - - private void validateCRUDCheckPresent(final ApexNode node, final Object data, final String crudMethod, + private boolean validateCRUDCheckPresent(final ApexNode node, final Object data, final String crudMethod, final String typeCheck) { boolean missingKey = !typeToDMLOperationMapping.containsKey(typeCheck); - boolean isImproperDMLCheck = !isProperESAPICheckForDML(typeCheck, crudMethod); + boolean isImproperDMLCheck = !isProperESAPICheckForDML(typeCheck, crudMethod) + && !isProperAuthPatternBasedCheckForDML(typeCheck, crudMethod); boolean noSecurityEnforced = !isWithSecurityEnforced(node); if (missingKey) { //if condition returns true, add violation, otherwise return. if (isImproperDMLCheck && noSecurityEnforced) { addViolation(data, node); + return true; } } else { boolean properChecksHappened = false; - List dmlOperationsChecked = typeToDMLOperationMapping.get(typeCheck); + Set dmlOperationsChecked = typeToDMLOperationMapping.get(typeCheck); for (String dmlOp : dmlOperationsChecked) { if (dmlOp.equalsIgnoreCase(crudMethod)) { properChecksHappened = true; @@ -493,11 +655,16 @@ public class ApexCRUDViolationRule extends AbstractApexRule { if (!properChecksHappened) { addViolation(data, node); + return true; } } + return false; } private void checkForAccessibility(final ASTSoqlExpression node, Object data) { + // TODO: This includes sub-relation queries which are incorrectly flagged because you authorize the type + // and not the sub-relation name. Should we (optionally) exclude sub-relations until/unless they can be + // resolved to the proper SObject type? final Set typesFromSOQL = getTypesFromSOQLQuery(node); final Set prevCalls = getPreviousMethodCalls(node); @@ -518,7 +685,7 @@ public class ApexCRUDViolationRule extends AbstractApexRule { if (wrappingMethod != null) { returnType = getReturnType(wrappingMethod); } - + boolean violationAdded = false; final ASTVariableDeclaration variableDecl = node.getFirstParentOfType(ASTVariableDeclaration.class); if (variableDecl != null) { String type = variableDecl.getType(); @@ -527,13 +694,17 @@ public class ApexCRUDViolationRule extends AbstractApexRule { .append(":").append(type); if (typesFromSOQL.isEmpty()) { - validateCRUDCheckPresent(node, data, ANY, typeCheck.toString()); + violationAdded = validateCRUDCheckPresent(node, data, ANY, typeCheck.toString()); } else { for (String typeFromSOQL : typesFromSOQL) { - validateCRUDCheckPresent(node, data, ANY, typeFromSOQL); + violationAdded |= validateCRUDCheckPresent(node, data, ANY, typeFromSOQL); } } + } + // If the node's already in violation, we don't need to keep checking. + if (violationAdded) { + return; } final ASTAssignmentExpression assignment = node.getFirstParentOfType(ASTAssignmentExpression.class); @@ -544,10 +715,10 @@ public class ApexCRUDViolationRule extends AbstractApexRule { if (varToTypeMapping.containsKey(variableWithClass)) { String type = varToTypeMapping.get(variableWithClass); if (typesFromSOQL.isEmpty()) { - validateCRUDCheckPresent(node, data, ANY, type); + violationAdded = validateCRUDCheckPresent(node, data, ANY, type); } else { for (String typeFromSOQL : typesFromSOQL) { - validateCRUDCheckPresent(node, data, ANY, typeFromSOQL); + violationAdded |= validateCRUDCheckPresent(node, data, ANY, typeFromSOQL); } } } @@ -555,10 +726,41 @@ public class ApexCRUDViolationRule extends AbstractApexRule { } + // If the node's already in violation, we don't need to keep checking. + if (violationAdded) { + return; + } + final ASTReturnStatement returnStatement = node.getFirstParentOfType(ASTReturnStatement.class); if (returnStatement != null) { if (typesFromSOQL.isEmpty()) { - validateCRUDCheckPresent(node, data, ANY, returnType); + violationAdded = validateCRUDCheckPresent(node, data, ANY, returnType); + } else { + for (String typeFromSOQL : typesFromSOQL) { + violationAdded |= validateCRUDCheckPresent(node, data, ANY, typeFromSOQL); + } + } + } + + // If the node's already in violation, we don't need to keep checking. + if (violationAdded) { + return; + } + + final ASTForEachStatement forEachStatement = node.getFirstParentOfType(ASTForEachStatement.class); + if (forEachStatement != null) { + if (typesFromSOQL.isEmpty()) { + + final ASTVariableDeclaration variableDeclFor = forEachStatement.getFirstParentOfType(ASTVariableDeclaration.class); + if (variableDeclFor != null) { + String type = variableDeclFor.getType(); + type = getSimpleType(type); + StringBuilder typeCheck = new StringBuilder().append(variableDeclFor.getDefiningType()) + .append(":").append(type); + + validateCRUDCheckPresent(node, data, ANY, typeCheck.toString()); + } + } else { for (String typeFromSOQL : typesFromSOQL) { validateCRUDCheckPresent(node, data, ANY, typeFromSOQL); @@ -583,4 +785,119 @@ public class ApexCRUDViolationRule extends AbstractApexRule { return new StringBuilder().append(method.getDefiningType()).append(":") .append(method.getReturnType()).toString(); } + + // Configured authorization method pattern support + + private static PropertyDescriptor authMethodPatternProperty(String operation) { + final String propertyName = operation + "AuthMethodPattern"; + return stringProperty(propertyName) + .desc("A regular expression for one or more custom " + operation + " authorization method name patterns.") + .defaultValue("") + .build(); + } + + private static PropertyDescriptor authMethodTypeParamIndexProperty(String operation) { + final String propertyName = operation + "AuthMethodTypeParamIndex"; + return intProperty(propertyName) + .desc("The 0-based index of the " + S_OBJECT_TYPE + " parameter for the custom " + operation + " authorization method. Defaults to 0.") + .defaultValue(0) + .build(); + } + + private boolean isAuthMethodInvocation(final ASTMethodCallExpression methodNode) { + for (PropertyDescriptor authMethodPatternDescriptor : AUTH_METHOD_TO_TYPE_PARAM_INDEX_MAP.keySet()) { + if (isAuthMethodInvocation(methodNode, authMethodPatternDescriptor)) { + return true; + } + } + return false; + } + + private void extractObjectTypeFromConfiguredMethodPatternInvocation(final ASTMethodCallExpression methodNode, final PropertyDescriptor authMethodPatternDescriptor) { + if (isAuthMethodInvocation(methodNode, authMethodPatternDescriptor)) { + // See which parameter index contains the object type expression and try to find that invocation argument + final PropertyDescriptor authMethodTypeParamIndexDescriptor = AUTH_METHOD_TO_TYPE_PARAM_INDEX_MAP.get(authMethodPatternDescriptor); + final Integer authMethodTypeParamIndex = authMethodTypeParamIndexDescriptor != null ? getProperty(authMethodTypeParamIndexDescriptor) : 0; + final int numParameters = methodNode.getInputParametersSize(); + if (numParameters > authMethodTypeParamIndex) { + final List parameters = new ArrayList<>(numParameters); + for (int parameterIndex = 0, numChildren = methodNode.getNumChildren(); parameterIndex < numChildren; parameterIndex++) { + final ApexNode childNode = methodNode.getChild(parameterIndex); + if (childNode instanceof ASTVariableExpression) { + parameters.add((ASTVariableExpression) childNode); + } + } + // Make sure that it looks like "sObjectType." as VariableExpression > ReferenceExpression + final ASTVariableExpression sobjectTypeParameterCandidate = parameters.size() > authMethodTypeParamIndex ? parameters.get(authMethodTypeParamIndex) : null; + if (sobjectTypeParameterCandidate != null && S_OBJECT_TYPE.equalsIgnoreCase(sobjectTypeParameterCandidate.getImage())) { + final ASTReferenceExpression objectTypeCandidate = sobjectTypeParameterCandidate.getFirstChildOfType(ASTReferenceExpression.class); + if (objectTypeCandidate != null) { + final String objectType = objectTypeCandidate.getImage(); + if (StringUtils.isNotBlank(objectType)) { + // Create a (relatively) unique key for this that is prefixed by the current invocation's containing type name + final StringBuilder checkedTypeBuilder = new StringBuilder().append(methodNode.getDefiningType()) + .append(":").append(objectType); + final String checkedType = checkedTypeBuilder.toString(); + + // And get the appropriate DML operation based on this method pattern + final String dmlOperation = AUTH_METHOD_TO_DML_OPERATION_MAP.get(authMethodPatternDescriptor); + if (StringUtils.isNotBlank(dmlOperation)) { + checkedTypeToDMLOperationsViaAuthPattern.put(checkedType, dmlOperation); + } + } + } + } + } + } + } + + private boolean isAuthMethodInvocation(final ASTMethodCallExpression methodNode, final PropertyDescriptor authMethodPatternDescriptor) { + final String authMethodPattern = getProperty(authMethodPatternDescriptor); + final Pattern compiledAuthMethodPattern = getCompiledAuthMethodPattern(authMethodPattern); + if (compiledAuthMethodPattern != null) { + final String fullMethodName = methodNode.getFullMethodName(); + final Matcher authMethodMatcher = compiledAuthMethodPattern.matcher(fullMethodName); + if (authMethodMatcher.matches()) { + return true; + } + } + return false; + } + + private Pattern getCompiledAuthMethodPattern(final String authMethodPattern) { + Pattern compiledAuthMethodPattern = null; + + if (StringUtils.isNotBlank(authMethodPattern)) { + // If we haven't previously tried to to compile this pattern, do so now + if (!compiledAuthMethodPatternCache.containsKey(authMethodPattern)) { + try { + compiledAuthMethodPattern = Pattern.compile(authMethodPattern, Pattern.CASE_INSENSITIVE); + compiledAuthMethodPatternCache.put(authMethodPattern, compiledAuthMethodPattern); + } catch (IllegalArgumentException e) { + // Cache a null value so we don't try to compile this particular pattern again + compiledAuthMethodPatternCache.put(authMethodPattern, null); + throw e; + } + } else { + // Otherwise use the cached value, either the successfully compiled pattern or null if pattern compilation failed + compiledAuthMethodPattern = compiledAuthMethodPatternCache.get(authMethodPattern); + } + } + + return compiledAuthMethodPattern; + } + + private boolean isProperAuthPatternBasedCheckForDML(final String typeToCheck, final String dmlOperation) { + final boolean hasMapping = checkedTypeToDMLOperationsViaAuthPattern.containsKey(typeToCheck); + if (hasMapping) { + if (ANY.equals(dmlOperation)) { + return true; + } + + final Set dmlOperationsChecked = checkedTypeToDMLOperationsViaAuthPattern.get(typeToCheck); + return dmlOperationsChecked.contains(dmlOperation); + } + + return false; + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/Helper.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/Helper.java index b51c0c2f68..7a9af5e824 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/Helper.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/Helper.java @@ -4,15 +4,8 @@ package net.sourceforge.pmd.lang.apex.rule.security; -import java.util.Arrays; import java.util.List; -import net.sourceforge.pmd.lang.apex.ast.ASTDmlDeleteStatement; -import net.sourceforge.pmd.lang.apex.ast.ASTDmlInsertStatement; -import net.sourceforge.pmd.lang.apex.ast.ASTDmlMergeStatement; -import net.sourceforge.pmd.lang.apex.ast.ASTDmlUndeleteStatement; -import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpdateStatement; -import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpsertStatement; import net.sourceforge.pmd.lang.apex.ast.ASTField; import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression; @@ -62,17 +55,7 @@ public final class Helper { * @return true if found DML operations in node descendants */ static boolean foundAnyDML(final ApexNode node) { - - final List dmlUpsertStatement = node.findDescendantsOfType(ASTDmlUpsertStatement.class); - final List dmlUpdateStatement = node.findDescendantsOfType(ASTDmlUpdateStatement.class); - final List dmlUndeleteStatement = node - .findDescendantsOfType(ASTDmlUndeleteStatement.class); - final List dmlMergeStatement = node.findDescendantsOfType(ASTDmlMergeStatement.class); - final List dmlInsertStatement = node.findDescendantsOfType(ASTDmlInsertStatement.class); - final List dmlDeleteStatement = node.findDescendantsOfType(ASTDmlDeleteStatement.class); - - return !dmlUpsertStatement.isEmpty() || !dmlUpdateStatement.isEmpty() || !dmlUndeleteStatement.isEmpty() - || !dmlMergeStatement.isEmpty() || !dmlInsertStatement.isEmpty() || !dmlDeleteStatement.isEmpty(); + return net.sourceforge.pmd.lang.apex.rule.internal.Helper.foundAnyDML(node); } static boolean isMethodName(final ASTMethodCallExpression methodNode, final String className, @@ -89,25 +72,7 @@ public final class Helper { } static boolean isMethodCallChain(final ASTMethodCallExpression methodNode, final String... methodNames) { - String methodName = methodNames[methodNames.length - 1]; - if (Helper.isMethodName(methodNode, methodName)) { - final ASTReferenceExpression reference = methodNode.getFirstChildOfType(ASTReferenceExpression.class); - if (reference != null) { - final ASTMethodCallExpression nestedMethod = reference - .getFirstChildOfType(ASTMethodCallExpression.class); - if (nestedMethod != null) { - String[] newMethodNames = Arrays.copyOf(methodNames, methodNames.length - 1); - return isMethodCallChain(nestedMethod, newMethodNames); - } else { - String[] newClassName = Arrays.copyOf(methodNames, methodNames.length - 1); - if (newClassName.length == 1) { - return Helper.isMethodName(methodNode, newClassName[0], methodName); - } - } - } - } - - return false; + return net.sourceforge.pmd.lang.apex.rule.internal.Helper.isMethodCallChain(methodNode, methodNames); } static String getFQVariableName(final ASTVariableExpression variable) { diff --git a/pmd-apex/src/main/resources/category/apex/bestpractices.xml b/pmd-apex/src/main/resources/category/apex/bestpractices.xml index 192d77e7b2..9ba93ed63e 100644 --- a/pmd-apex/src/main/resources/category/apex/bestpractices.xml +++ b/pmd-apex/src/main/resources/category/apex/bestpractices.xml @@ -45,7 +45,8 @@ public class Foo { externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#apexunittestclassshouldhaveasserts"> Apex unit tests should include at least one assertion. This makes the tests more robust, and using assert -with messages provide the developer a clearer idea of what the test does. +with messages provide the developer a clearer idea of what the test does. Custom assert method invocation +patterns can be specified using the 'additionalAssertMethodPattern' property if required. 3 diff --git a/pmd-apex/src/main/resources/category/apex/codestyle.xml b/pmd-apex/src/main/resources/category/apex/codestyle.xml index cc3a357aea..0fd72a9408 100644 --- a/pmd-apex/src/main/resources/category/apex/codestyle.xml +++ b/pmd-apex/src/main/resources/category/apex/codestyle.xml @@ -282,7 +282,7 @@ can lead to quite messy code. This rule looks for several declarations on the sa 1] + [count(VariableDeclaration) > 1 and ($reportInForLoopInitializer = true() or name(parent::*) != 'ForLoopStatement')] [$strictMode or count(distinct-values(VariableDeclaration/@BeginLine)) != count(VariableDeclaration)] | //FieldDeclarationStatements @@ -293,6 +293,7 @@ can lead to quite messy code. This rule looks for several declarations on the sa + 3 + + @@ -213,10 +217,12 @@ Empty block statements serve no purpose and should be removed. 3 + + 0])]/ModifierNode[@Abstract != true() and ($reportEmptyVirtualMethod = true() or @Virtual != true()) and ../BlockStatement[count(*) = 0]] | //Method/BlockStatement//BlockStatement[count(*) = 0 and @RealLoc = true()] ]]> diff --git a/pmd-apex/src/main/resources/category/apex/performance.xml b/pmd-apex/src/main/resources/category/apex/performance.xml index 5e5d7eebd1..731a8e1aa3 100644 --- a/pmd-apex/src/main/resources/category/apex/performance.xml +++ b/pmd-apex/src/main/resources/category/apex/performance.xml @@ -137,6 +137,61 @@ public class Something { + + +This rule finds `DescribeSObjectResult`s which could have been loaded eagerly via `SObjectType.getDescribe()`. + +When using `SObjectType.getDescribe()` or `Schema.describeSObjects()` without supplying a `SObjectDescribeOptions`, implicitely it will be using `SObjectDescribeOptions.DEFAULT` then all +child relationships will be loaded eagerly regardless whether this information is needed or not. +This has a potential negative performance impact. Instead [`SObjectType.getDescribe(options)`](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_Schema_SObjectType.htm#unique_346834793) +or [`Schema.describeSObjects(SObjectTypes, options)`](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_schema.htm#apex_System_Schema_describeSObjects) should be used and a `SObjectDescribeOptions` should be supplied. By using +`SObjectDescribeOptions.DEFERRED` the describe attributes will be lazily initialized at first use. + +Lazy loading `DescribeSObjectResult` on picklist fields is not recommended. The lazy loaded +describe objects might not be 100% accurate. It might be safer to explicitly use +`SObjectDescribeOptions.FULL` in such a case. The same applies when you need the same `DescribeSObjectResult` to be consistent +accross different contexts and API versions. + +Properties: + +* `noDefault`: The behavior of `SObjectDescribeOptions.DEFAULT` changes from API Version 43 to 44: + With API Version 43, the attributes are loaded eagerly. With API Version 44, they are loaded lazily. + Simply using `SObjectDescribeOptions.DEFAULT` doesn't automatically make use of lazy loading. + (unless "Use Improved Schema Caching" critical update is applied, `SObjectDescribeOptions.DEFAULT` do fallback to lazy loading) + With this property enabled, such usages are found. + You might ignore this, if you can make sure, that you don't run a mix of API Versions. + + 3 + + + + + + + + + + + accounts) { + if (Account.SObjectType.getDescribe(SObjectDescribeOptions.DEFERRED).isCreateable()) { + insert accounts; + } + } +} +]]> + + + - + + 3 + + + + + + + + + +``` + Note: This rule will produce false positives for VF getter methods. In VF getters the access permission check happens automatically and is not needed explicitly. However, the rule can't reliably determine whether a getter is a VF getter or not and reports a violation in any case. In such cases, the violation should be [suppressed](pmd_userdocs_suppressing_warnings.html). +]]> 3 @@ -58,7 +103,7 @@ public class Foo { Contact c = [SELECT Status__c FROM Contact WHERE Id=:ID WITH SECURITY_ENFORCED]; // Make sure we can update the database before even trying - if (!Schema.sObjectType.Contact.fields.Name.isUpdateable()) { + if (!Schema.sObjectType.Contact.fields.Status__c.isUpdateable()) { return null; } diff --git a/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml b/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml index 19f6e84fb5..767abb2afd 100644 --- a/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml +++ b/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml @@ -84,6 +84,7 @@ 3 + diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexCommentTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexCommentTest.java new file mode 100644 index 0000000000..9652528256 --- /dev/null +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexCommentTest.java @@ -0,0 +1,23 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import org.junit.Assert; +import org.junit.Test; + +public class ApexCommentTest extends ApexParserTestBase { + + + @Test + public void testContainsComment1() { + ASTApexFile file = apex.parse("class Foo {void foo(){try {\n" + + "} catch (Exception e) {\n" + + " /* OK: block comment inside of empty catch block; should not be reported */\n" + + "}}}"); + + ASTCatchBlockStatement catchBlock = file.descendants(ASTCatchBlockStatement.class).crossFindBoundaries().firstOrThrow(); + Assert.assertTrue(catchBlock.getContainsComment()); + } +} diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java index 3210720966..4aee3a0fe7 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java @@ -45,6 +45,15 @@ public class ApexParserTest extends ApexParserTestBase { assertEquals(4, methods.size()); } + @Test + public void fileName() { + String code = "class Outer { class Inner {}}"; + + ASTUserClass rootNode = (ASTUserClass) parse(code, "src/filename.cls"); + + assertEquals("src/filename.cls", rootNode.getTextDocument().getDisplayName()); + } + private final String testCodeForLineNumbers = "public class SimpleClass {\n" // line 1 + " public void method1() {\n" // line 2 diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTestBase.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTestBase.java index 71c58ae95f..6ad7de0e66 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTestBase.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTestBase.java @@ -13,6 +13,10 @@ public class ApexParserTestBase { return apex.parse(code).getMainNode(); } + protected ASTUserClassOrInterface parse(String code, String fileName) { + return apex.parse(code, null, fileName).getMainNode(); + } + protected ASTUserClassOrInterface parseResource(String code) { return apex.parseResource(code).getMainNode(); } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserXPathTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserXPathTest.java deleted file mode 100644 index af43119509..0000000000 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserXPathTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.apex.ast; - -import java.nio.charset.StandardCharsets; -import java.util.List; - -import org.apache.commons.io.IOUtils; -import org.junit.Assert; -import org.junit.Test; - -import net.sourceforge.pmd.lang.ast.Node; - -public class ApexParserXPathTest extends ApexParserTestBase { - - @Test - public void testBooleanExpressions() throws Exception { - ApexNode node = parse(IOUtils.toString(ApexParserXPathTest.class.getResourceAsStream("BooleanExpressions.cls"), - StandardCharsets.UTF_8)); - List booleanExpressions = node.descendants(ASTBooleanExpression.class).toList(); - Assert.assertEquals(2, booleanExpressions.size()); - Assert.assertEquals("&&", booleanExpressions.get(0).getOperator().toString()); - Assert.assertEquals("!=", booleanExpressions.get(1).getOperator().toString()); - - List xpathResult = node.findChildNodesWithXPath("//BooleanExpression[@Operator='&&']"); - Assert.assertEquals(1, xpathResult.size()); - Assert.assertSame(booleanExpressions.get(0), xpathResult.get(0)); - } -} diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeDumpTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeDumpTest.java index dc7c66b6f1..6ce49d352e 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeDumpTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeDumpTest.java @@ -23,7 +23,7 @@ public class ApexTreeDumpTest extends BaseTreeDumpTest { } @Test - public void safeNavigationOperator() throws Exception { + public void safeNavigationOperator() { doTest("SafeNavigationOperator"); } } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/ApexXPathRuleTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/ApexXPathRuleTest.java new file mode 100644 index 0000000000..b90d4a26ce --- /dev/null +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/ApexXPathRuleTest.java @@ -0,0 +1,48 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.rule; + +import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertSize; + +import org.junit.Test; + +import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.apex.ApexLanguageModule; +import net.sourceforge.pmd.lang.apex.ast.ApexParserTestBase; +import net.sourceforge.pmd.lang.rule.XPathRule; +import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; + +/** + * @author daniels + */ +public class ApexXPathRuleTest extends ApexParserTestBase { + + private XPathRule makeXPath(String expression) { + XPathRule rule = new XPathRule(XPathVersion.XPATH_2_0, expression); + rule.setLanguage(LanguageRegistry.getLanguage(ApexLanguageModule.NAME)); + rule.setMessage("XPath Rule Failed"); + return rule; + } + + + @Test + public void testFileNameInXpath() { + Report report = apex.executeRule(makeXPath("/UserClass[pmd:fileName() = 'Foo.cls']"), + "class Foo {}", + "src/Foo.cls"); + + assertSize(report, 1); + } + + @Test + public void testBooleanExpressions() { + Report report = apex.executeRuleOnResource(makeXPath("//BooleanExpression[@Operator='&&']"), + "BooleanExpressions.cls"); + assertSize(report, 1); + } + + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertEqualsInsteadOfAssertTrueTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/performance/EagerlyLoadedDescribeSObjectResultTest.java similarity index 60% rename from pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertEqualsInsteadOfAssertTrueTest.java rename to pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/performance/EagerlyLoadedDescribeSObjectResultTest.java index 4df42d6789..3a50ee847e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertEqualsInsteadOfAssertTrueTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/performance/EagerlyLoadedDescribeSObjectResultTest.java @@ -2,10 +2,10 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.lang.java.rule.bestpractices; +package net.sourceforge.pmd.lang.apex.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -public class UseAssertEqualsInsteadOfAssertTrueTest extends PmdRuleTst { +public class EagerlyLoadedDescribeSObjectResultTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/SafeNavigationOperator.txt b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/SafeNavigationOperator.txt index a9fd23b1f5..e10be6e63f 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/SafeNavigationOperator.txt +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/SafeNavigationOperator.txt @@ -1,99 +1,99 @@ -+- ApexFile[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - +- UserClass[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "Foo", @Namespace = "", @RealLoc = "true", @SimpleName = "Foo", @SuperClassName = ""] - +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - +- Field[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Name = "x", @Namespace = "", @RealLoc = "true", @Type = "Integer", @Value = null] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - +- Field[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "profileUrl", @Name = "profileUrl", @Namespace = "", @RealLoc = "true", @Type = "String", @Value = null] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - +- FieldDeclarationStatements[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true", @TypeName = "Integer"] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | +- FieldDeclaration[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "anIntegerField", @Name = "anIntegerField", @Namespace = "", @RealLoc = "true"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "anIntegerField", @Namespace = "", @RealLoc = "true"] - | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] - | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "anObject", @Namespace = "", @RealLoc = "true"] - | | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Namespace = "", @RealLoc = "true"] - | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - +- FieldDeclarationStatements[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true", @TypeName = "String"] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | +- FieldDeclaration[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "profileUrl", @Name = "profileUrl", @Namespace = "", @RealLoc = "true"] - | +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "toExternalForm", @InputParametersSize = "0", @MethodName = "toExternalForm", @Namespace = "", @RealLoc = "true"] - | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"] - | | +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "user.getProfileUrl", @InputParametersSize = "0", @MethodName = "getProfileUrl", @Namespace = "", @RealLoc = "true"] - | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Image = "user", @Namespace = "", @RealLoc = "true", @ReferenceType = "METHOD", @SafeNav = "false"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "profileUrl", @Namespace = "", @RealLoc = "true"] - | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - +- Method[@ApexVersion = "51.0", @Arity = "1", @CanonicalName = "bar1", @Constructor = "false", @DefiningType = "Foo", @Image = "bar1", @Namespace = "", @RealLoc = "true", @ReturnType = "void", @Synthetic = "false"] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | +- Parameter[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true", @Type = "Object"] - | | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | +- BlockStatement[@ApexVersion = "51.0", @CurlyBrace = "true", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | +- ExpressionStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "b", @Namespace = "", @RealLoc = "true"] - | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] - | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true"] - | | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - | +- ExpressionStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "c1", @InputParametersSize = "0", @MethodName = "c1", @Namespace = "", @RealLoc = "true"] - | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"] - | +- CastExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "b1", @Namespace = "", @RealLoc = "true"] - | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a1", @Namespace = "", @RealLoc = "true"] - | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - +- Method[@ApexVersion = "51.0", @Arity = "2", @CanonicalName = "bar2", @Constructor = "false", @DefiningType = "Foo", @Image = "bar2", @Namespace = "", @RealLoc = "true", @ReturnType = "void", @Synthetic = "false"] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | +- Parameter[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true", @Type = "List"] - | | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | +- Parameter[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Namespace = "", @RealLoc = "true", @Type = "int"] - | | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | +- BlockStatement[@ApexVersion = "51.0", @CurlyBrace = "true", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | +- ExpressionStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "aField", @Namespace = "", @RealLoc = "true"] - | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "false"] - | | +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "aMethod", @InputParametersSize = "0", @MethodName = "aMethod", @Namespace = "", @RealLoc = "true"] - | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"] - | | +- ArrayLoadExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true"] - | | | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Namespace = "", @RealLoc = "true"] - | | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - | +- ExpressionStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "aField", @Namespace = "", @RealLoc = "true"] - | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] - | +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "aMethod", @InputParametersSize = "0", @MethodName = "aMethod", @Namespace = "", @RealLoc = "true"] - | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "false"] - | +- ArrayLoadExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true"] - | | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Namespace = "", @RealLoc = "true"] - | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - +- Method[@ApexVersion = "51.0", @Arity = "1", @CanonicalName = "getName", @Constructor = "false", @DefiningType = "Foo", @Image = "getName", @Namespace = "", @RealLoc = "true", @ReturnType = "String", @Synthetic = "false"] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | +- Parameter[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "accId", @Namespace = "", @RealLoc = "true", @Type = "int"] - | | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | +- BlockStatement[@ApexVersion = "51.0", @CurlyBrace = "true", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | +- VariableDeclarationStatements[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - | | +- VariableDeclaration[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "s", @Namespace = "", @RealLoc = "true", @Type = "String"] - | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "BillingCity", @Namespace = "", @RealLoc = "true"] - | | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] - | | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "Account", @Namespace = "", @RealLoc = "true"] - | | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Image = "contact", @Namespace = "", @RealLoc = "true", @ReferenceType = "LOAD", @SafeNav = "false"] - | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "s", @Namespace = "", @RealLoc = "true"] - | | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - | +- ReturnStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "Name", @Namespace = "", @RealLoc = "true"] - | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] - | +- SoqlExpression[@ApexVersion = "51.0", @CanonicalQuery = "SELECT Name FROM Account WHERE Id = :tmpVar1", @DefiningType = "Foo", @Namespace = "", @Query = "SELECT Name FROM Account WHERE Id = :accId", @RealLoc = "true"] - | +- BindExpressions[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] - | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "accId", @Namespace = "", @RealLoc = "true"] - | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] - +- Method[@ApexVersion = "51.0", @Arity = "0", @CanonicalName = "", @Constructor = "false", @DefiningType = "Foo", @Image = "", @Namespace = "", @RealLoc = "false", @ReturnType = "void", @Synthetic = "true"] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "8", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "true", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - +- Method[@ApexVersion = "51.0", @Arity = "0", @CanonicalName = "clone", @Constructor = "false", @DefiningType = "Foo", @Image = "clone", @Namespace = "", @RealLoc = "false", @ReturnType = "Object", @Synthetic = "true"] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "true", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - +- UserClassMethods[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "false"] - | +- Method[@ApexVersion = "51.0", @Arity = "0", @CanonicalName = "", @Constructor = "true", @DefiningType = "Foo", @Image = "", @Namespace = "", @RealLoc = "false", @ReturnType = "void", @Synthetic = "true"] - | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "true", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] - +- BridgeMethodCreator[@ApexVersion = "51.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "false"] ++- ApexFile[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + +- UserClass[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "Foo", @Namespace = "", @RealLoc = "true", @SimpleName = "Foo", @SuperClassName = ""] + +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + +- Field[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "x", @Name = "x", @Namespace = "", @RealLoc = "true", @Type = "Integer", @Value = null] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + +- Field[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "profileUrl", @Name = "profileUrl", @Namespace = "", @RealLoc = "true", @Type = "String", @Value = null] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + +- FieldDeclarationStatements[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true", @TypeName = "Integer"] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | +- FieldDeclaration[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "anIntegerField", @Name = "anIntegerField", @Namespace = "", @RealLoc = "true"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "anIntegerField", @Namespace = "", @RealLoc = "true"] + | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] + | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "anObject", @Namespace = "", @RealLoc = "true"] + | | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "x", @Namespace = "", @RealLoc = "true"] + | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + +- FieldDeclarationStatements[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true", @TypeName = "String"] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | +- FieldDeclaration[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "profileUrl", @Name = "profileUrl", @Namespace = "", @RealLoc = "true"] + | +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "toExternalForm", @InputParametersSize = "0", @MethodName = "toExternalForm", @Namespace = "", @RealLoc = "true"] + | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"] + | | +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "user.getProfileUrl", @InputParametersSize = "0", @MethodName = "getProfileUrl", @Namespace = "", @RealLoc = "true"] + | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Image = "user", @Namespace = "", @RealLoc = "true", @ReferenceType = "METHOD", @SafeNav = "false"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "profileUrl", @Namespace = "", @RealLoc = "true"] + | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + +- Method[@ApexVersion = "54.0", @Arity = "1", @CanonicalName = "bar1", @Constructor = "false", @DefiningType = "Foo", @Image = "bar1", @Namespace = "", @RealLoc = "true", @ReturnType = "void", @Synthetic = "false"] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | +- Parameter[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true", @Type = "Object"] + | | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | +- BlockStatement[@ApexVersion = "54.0", @CurlyBrace = "true", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | +- ExpressionStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "b", @Namespace = "", @RealLoc = "true"] + | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] + | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true"] + | | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + | +- ExpressionStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "c1", @InputParametersSize = "0", @MethodName = "c1", @Namespace = "", @RealLoc = "true"] + | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"] + | +- CastExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "b1", @Namespace = "", @RealLoc = "true"] + | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a1", @Namespace = "", @RealLoc = "true"] + | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + +- Method[@ApexVersion = "54.0", @Arity = "2", @CanonicalName = "bar2", @Constructor = "false", @DefiningType = "Foo", @Image = "bar2", @Namespace = "", @RealLoc = "true", @ReturnType = "void", @Synthetic = "false"] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | +- Parameter[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true", @Type = "List"] + | | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | +- Parameter[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "x", @Namespace = "", @RealLoc = "true", @Type = "int"] + | | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | +- BlockStatement[@ApexVersion = "54.0", @CurlyBrace = "true", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | +- ExpressionStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "aField", @Namespace = "", @RealLoc = "true"] + | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "false"] + | | +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "aMethod", @InputParametersSize = "0", @MethodName = "aMethod", @Namespace = "", @RealLoc = "true"] + | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"] + | | +- ArrayLoadExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true"] + | | | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "x", @Namespace = "", @RealLoc = "true"] + | | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + | +- ExpressionStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "aField", @Namespace = "", @RealLoc = "true"] + | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] + | +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "aMethod", @InputParametersSize = "0", @MethodName = "aMethod", @Namespace = "", @RealLoc = "true"] + | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "false"] + | +- ArrayLoadExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a", @Namespace = "", @RealLoc = "true"] + | | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "x", @Namespace = "", @RealLoc = "true"] + | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + +- Method[@ApexVersion = "54.0", @Arity = "1", @CanonicalName = "getName", @Constructor = "false", @DefiningType = "Foo", @Image = "getName", @Namespace = "", @RealLoc = "true", @ReturnType = "String", @Synthetic = "false"] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | +- Parameter[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "accId", @Namespace = "", @RealLoc = "true", @Type = "int"] + | | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | +- BlockStatement[@ApexVersion = "54.0", @CurlyBrace = "true", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | +- VariableDeclarationStatements[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + | | +- VariableDeclaration[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "s", @Namespace = "", @RealLoc = "true", @Type = "String"] + | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "BillingCity", @Namespace = "", @RealLoc = "true"] + | | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] + | | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "Account", @Namespace = "", @RealLoc = "true"] + | | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Image = "contact", @Namespace = "", @RealLoc = "true", @ReferenceType = "LOAD", @SafeNav = "false"] + | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "s", @Namespace = "", @RealLoc = "true"] + | | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + | +- ReturnStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "Name", @Namespace = "", @RealLoc = "true"] + | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"] + | +- SoqlExpression[@ApexVersion = "54.0", @CanonicalQuery = "SELECT Name FROM Account WHERE Id = :tmpVar1", @DefiningType = "Foo", @Namespace = "", @Query = "SELECT Name FROM Account WHERE Id = :accId", @RealLoc = "true"] + | +- BindExpressions[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "true"] + | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "accId", @Namespace = "", @RealLoc = "true"] + | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Namespace = null, @RealLoc = "false"] + +- Method[@ApexVersion = "54.0", @Arity = "0", @CanonicalName = "", @Constructor = "false", @DefiningType = "Foo", @Image = "", @Namespace = "", @RealLoc = "false", @ReturnType = "void", @Synthetic = "true"] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Modifiers = "8", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "true", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + +- Method[@ApexVersion = "54.0", @Arity = "0", @CanonicalName = "clone", @Constructor = "false", @DefiningType = "Foo", @Image = "clone", @Namespace = "", @RealLoc = "false", @ReturnType = "Object", @Synthetic = "true"] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "true", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + +- UserClassMethods[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "false"] + | +- Method[@ApexVersion = "54.0", @Arity = "0", @CanonicalName = "", @Constructor = "true", @DefiningType = "Foo", @Image = "", @Namespace = "", @RealLoc = "false", @ReturnType = "void", @Synthetic = "true"] + | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "true", @InheritedSharing = "false", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @Virtual = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"] + +- BridgeMethodCreator[@ApexVersion = "54.0", @DefiningType = "Foo", @Namespace = "", @RealLoc = "false"] diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/BooleanExpressions.cls b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/BooleanExpressions.cls similarity index 100% rename from pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/BooleanExpressions.cls rename to pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/BooleanExpressions.cls diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexUnitTestClassShouldHaveAsserts.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexUnitTestClassShouldHaveAsserts.xml index 36bc698610..8e9520f15d 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexUnitTestClassShouldHaveAsserts.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/ApexUnitTestClassShouldHaveAsserts.xml @@ -70,6 +70,48 @@ private class C2_Assignment_Report_Job_Test { System.assertEquals(1, userIdWithRole2ReportData.get(createUserRoleKey(u.Id, 'QR-CSA'))); } } +} + ]]> + + + + #1089 [apex] ApexUnitTestClassShouldHaveAsserts: Verify use of additionalAssertMethodPattern, positive test + (Assert\.\w+|verify\w+) + 0 + + + + + #1089 [apex] ApexUnitTestClassShouldHaveAsserts: Verify use of additionalAssertMethodPattern, negative test + 2 + 3,7 + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/OneDeclarationPerLine.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/OneDeclarationPerLine.xml index d1e533d7d9..c2e1dacfbe 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/OneDeclarationPerLine.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/OneDeclarationPerLine.xml @@ -90,4 +90,45 @@ public class Foo { } ]]> + + + #3570 - Verify use of reportInForLoopInitializer, negative test unspecified/default + 1 + + + + + #3570 - Verify use of reportInForLoopInitializer, negative test specified + true + 1 + + + + + #3570 - Verify use of reportInForLoopInitializer, positive test + false + 0 + + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml index 0bd349a322..76a010069f 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml @@ -571,6 +571,63 @@ public class Foo { public class Bar { } +} + ]]> + + + + #3566 [apex] ApexDoc: Verify use of reportMissingDescription, negative test unspecified/default + + 2 + + + + + #3566 [apex] ApexDoc: Verify use of reportMissingDescription, negative test specified + true + 2 + + + + + #3566 [apex] ApexDoc: Verify use of reportMissingDescription, positive test + false + 0 + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/EmptyCatchBlock.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/EmptyCatchBlock.xml index 5362633e92..8c44fb49c7 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/EmptyCatchBlock.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/EmptyCatchBlock.xml @@ -34,4 +34,306 @@ public class Foo { } ]]> + + + #3569 - Verify use of allowExceptionNameRegex, negative test unspecified + 2 + 7,16 + + + + + #3569 - Verify use of allowExceptionNameRegex, negative test specified + ignoreMe + 2 + 7,16 + + + + + #3569 - Verify use of allowExceptionNameRegex, positive test unspecified + 0 + + + + + #3569 - Verify use of allowExceptionNameRegex, positive test specified + ignoreMe + 0 + + + + + #3569 - Verify use of allowCommentedBlocks, negative test unspecified/default + 2 + 7,17 + + + + + #3569 - Verify use of allowCommentedBlocks, negative test specified + false + 2 + 7,17 + + + + + #3569 - Verify use of allowCommentedBlocks, positive test + true + 0 + + + + + + + #3569 - Verify use of allowCommentedBlocks=false + false + 16 + 8,13,19,23,30,35,43,47,51,54,58,65,70,78,82,86 + + + + + #3569 - Verify use of allowCommentedBlocks=true, binary search boundaries verification + true + 4 + 19,23,54,58 + + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/EmptyStatementBlock.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/EmptyStatementBlock.xml index 0bc5a4c41a..024b8a0132 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/EmptyStatementBlock.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/EmptyStatementBlock.xml @@ -58,4 +58,74 @@ public class Foo { } ]]> + + + #3568 - Verify use of reportEmptyPrivateNoArgConstructor, negative test unspecified/default + 1 + + + + + #3568 - Verify use of reportEmptyPrivateNoArgConstructor, negative test specified + true + 1 + + + + + #3568 - Verify use of reportEmptyPrivateNoArgConstructor, positive test + false + 0 + + + + + #3568 - Verify use of reportEmptyVirtualMethod, negative test unspecified/default + 1 + + + + + #3568 - Verify use of reportEmptyVirtualMethod, negative test specified + true + 1 + + + + + #3568 - Verify use of reportEmptyVirtualMethod, positive test + false + 0 + + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/EagerlyLoadedDescribeSObjectResult.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/EagerlyLoadedDescribeSObjectResult.xml new file mode 100644 index 0000000000..c8f7aeb7ad --- /dev/null +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/EagerlyLoadedDescribeSObjectResult.xml @@ -0,0 +1,92 @@ + + + + + No describer options + 1 + accounts) { + if (Account.SObjectType.getDescribe().isCreateable()) { + insert accounts; + } + } +} + ]]> + + + + No describer options using Schema class + 1 + accounts) { + if (Schema.describeSObjects(new List{'Account'})[0].isCreateable()) { + insert accounts; + } + } +} + ]]> + + + + Correct describer option check + 0 + accounts) { + if (Account.SObjectType.getDescribe(SObjectDescribeOptions.DEFERRED).isCreateable()) { + insert accounts; + } + } +} + ]]> + + + + Correct describer option check using Schema class + 0 + accounts) { + if (Schema.describeSObjects(new List{'Account'}, SObjectDescribeOptions.DEFERRED)[0].isCreateable()) { + insert accounts; + } + } +} + ]]> + + + + Default describer option check allowing default + 0 + accounts) { + if (Account.SObjectType.getDescribe(SObjectDescribeOptions.DEFAULT).isCreateable()) { + insert accounts; + } + } +} + ]]> + + + + Default describer option check restricting default option + true + 1 + accounts) { + if (Account.SObjectType.getDescribe(SObjectDescribeOptions.DEFAULT).isCreateable()) { + insert accounts; + } + } +} + ]]> + + + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml index 0a8f58177b..5d5234e28a 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml @@ -134,6 +134,32 @@ public class Foo { ]]> + + Proper CRUD,FLS via upsert with database class + 0 + + + + + No CRUD check for inline upsert with database class + 2 + + + VF built-in CRUD checks via getter, but cannot determine if is really VF 2 @@ -600,6 +626,40 @@ public class Foo { ]]> + + No CRUD,FLS check for update with database class + 2 + + + + + Proper CRUD,FLS check for update with database class + 0 + + + No CRUD check for insert 1 @@ -628,6 +688,34 @@ public class Foo { ]]> + + No CRUD check for insert with database class + 1 + + + + + Proper CRUD check for insert with database class + 0 + + + No CRUD check for delete 2 @@ -658,6 +746,36 @@ public class Foo { ]]> + + No CRUD check for delete with database class + 2 + + + + + Proper CRUD check for delete with database class + 0 + + + Proper CRUD check for a list of sObject 0 @@ -913,6 +1031,499 @@ public class Foo { void chainedMethods() { firstMethod().lastMethod(); } +} + ]]> + + + + No CRUD check in SOQL for-loop + 1 + + + + + No CRUD check inside for-each loop + 1 + (); + for (Id accId : accIds) { + Account acc = [SELECT Id FROM Account WHERE Id = :accId]; + } + } +} + ]]> + + + + Proper CRUD check inside for-each loop + 0 + (); + if (Account.sObjectType.getDescribe().isAccessible()) { + for (Id accId : accIds) { + Account a = [SELECT Id FROM Account WHERE Id = :accId]; + } + } + } +} + ]]> + + + + Proper CRUD check in SOQL for-loop with security enforced + 0 + + + + + Proper CRUD check in SOQL for-loop with explicit check + 0 + + + + + No CRUD,FLS for inline no-args object + 1 + + + + + Proper CRUD,FLS for inline no-args object + 0 + + + + + No CRUD,FLS for inline literal list + 1 + + + + + Proper CRUD,FLS for inline literal list + 0 + + + + + No CRUD,FLS for inline initialized list + 1 + (new Account(Name = 'X')); + } +} + ]]> + + + + Proper CRUD,FLS for inline initialized list + 0 + (new Account(Name = 'X')); + } + } +} + ]]> + + + + + + #3576 - Verify use of createAuthMethodPattern, negative test + 2 + 4,8 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertCreateable(Account.SObjectType); + Database.insert(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of createAuthMethodPattern, positive test + AuthorizationUtil\.(is|assert)Createable + 0 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertCreateable(Account.SObjectType); + Database.insert(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of readAuthMethodPattern, negative test + 2 + 4,9 + accounts = [SELECT Id FROM Account]; + } + + AuthorizationUtil.assertAccessible(Account.SObjectType); + // TODO: Evidently this rule doesn't check Database.query() yet + List accounts = [SELECT Id FROM Account]; + } +} + ]]> + + + + #3576 - Verify use of readAuthMethodPattern, positive test + AuthorizationUtil\.(is|assert)Accessible + 0 + accounts = [SELECT Id FROM Account]; + } + + AuthorizationUtil.assertAccessible(Account.SObjectType); + // TODO: Evidently this rule doesn't check Database.query() yet + List accounts = [SELECT Id FROM Account]; + } +} + ]]> + + + + #3576 - Verify use of updateAuthMethodPattern, negative test + 2 + 4,8 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertUpdateable(Account.SObjectType); + Database.update(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of updateAuthMethodPattern, positive test + AuthorizationUtil\.(is|assert)Updateable + 0 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertUpdateable(Account.SObjectType); + Database.update(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of createAuthMethodPattern and updateAuthMethodPattern for upsert, negative test + 4 + 4,4,8,8 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertUpsertable(Account.SObjectType); + Database.upsert(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of createAuthMethodPattern and updateAuthMethodPattern for upsert, positive test + AuthorizationUtil\.(is|assert)(Createable|Upsertable) + AuthorizationUtil\.(is|assert)(Updateable|Upsertable) + 0 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertUpsertable(Account.SObjectType); + Database.upsert(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of deleteAuthMethodPattern, negative test + 2 + 4,8 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertDeletable(Account.SObjectType); + Database.delete(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of deleteAuthMethodPattern, positive test + AuthorizationUtil\.(is|assert)Deletable + 0 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertDeletable(Account.SObjectType); + Database.delete(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of undeleteAuthMethodPattern, negative test + 2 + 4,8 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertUndeletable(Account.SObjectType); + Database.undelete(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of undeleteAuthMethodPattern, positive test + AuthorizationUtil\.(is|assert)Undeletable + 0 + (new Account(Name = 'X')); + } + + AuthorizationUtil.assertUndeletable(Account.SObjectType); + Database.undelete(new List(new Account(Name = 'X'))); + } +} + ]]> + + + + #3576 - Verify use of mergeAuthMethodPattern, negative test + 2 + 7,11 + + + + + #3576 - Verify use of mergeAuthMethodPattern, positive test + AuthorizationUtil\.(is|assert)Mergeable + 0 + + + + + #3576 - Verify use of *AuthMethodTypeParamIndex + SomeAuthUtil\.canCreate\w+ + 1 + SomeAuthUtil\.canRead\w+ + 2 + SomeAuthUtil\.canUpdate\w+ + 3 + SomeAuthUtil\.canDelete\w+ + 4 + SomeAuthUtil\.canUndelete\w+ + 0 + SomeAuthUtil\.canMerge\w+ + + 0 + (new Account(Name = 'X')); + } + + if (SomeAuthUtil.canReadWithTypeAsThirdParam(param1, param2, Account.SObjectType)) { + List accounts = [SELECT Id FROM Account]; + } + + if (SomeAuthUtil.canUpdateWithTypeAsFourthParam(param1, param2, param3, Account.SObjectType)) { + update new List(new Account(Name = 'X')); + } + + if (SomeAuthUtil.canDeleteWithTypeAsFifthParam(param1, param2, param3, param4, Account.SObjectType)) { + delete new List(new Account(Name = 'X')); + } + + if (SomeAuthUtil.canUndeleteWithTypeAsFirstParamExplicit(Account.SObjectType)) { + undelete new List(new Account(Name = 'X')); + } + + Account masterAccount; + Account mergeAccount; + if (SomeAuthUtil.canMergeWithTypeAsFirstParamImplicit(Account.SObjectType)) { + merge masterAccount mergeAccount; + } + } +} + ]]> + + + + + + Demonstrate that authorization for sub-relation queries doesn't work properly #2775 + 0 + " + Contact.SObjectType.getDescribe().isAccessible()) { + List accounts = [ + SELECT Id, ( + SELECT Id + FROM Contacts + ) + FROM Account + ]; + } + } } ]]> diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java index 2f9602b1fc..099975ec95 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -7,6 +7,8 @@ package net.sourceforge.pmd; import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static net.sourceforge.pmd.util.CollectionUtil.map; +import java.io.File; +import java.io.FilenameFilter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; @@ -16,6 +18,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Map.Entry; import java.util.Set; import java.util.logging.ConsoleHandler; import java.util.logging.Level; @@ -402,7 +405,17 @@ public final class PMD { */ public static StatusCode runPmd(String... args) { PmdParametersParseResult parseResult = PmdParametersParseResult.extractParameters(args); - if (parseResult.isHelp()) { + + if (!parseResult.getDeprecatedOptionsUsed().isEmpty()) { + Entry first = parseResult.getDeprecatedOptionsUsed().entrySet().iterator().next(); + LOG.warning("Some deprecated options were used on the command-line, including " + first.getKey()); + LOG.warning("Consider replacing it with " + first.getValue()); + } + + if (parseResult.isVersion()) { + System.out.println("PMD " + PMDVersion.VERSION); + return StatusCode.OK; + } else if (parseResult.isHelp()) { PMDCommandLineInterface.printJcommanderUsageOnConsole(); System.out.println(PMDCommandLineInterface.buildUsageText()); return StatusCode.OK; @@ -504,4 +517,11 @@ public final class PMD { } } + + private static class AcceptAllFilenames implements FilenameFilter { + @Override + public boolean accept(File dir, String name) { + return true; + } + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index 9a369fdea5..ea69ec6007 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -96,6 +96,7 @@ public class PMDConfiguration extends AbstractConfiguration { private int threads = Runtime.getRuntime().availableProcessors(); private ClassLoader classLoader = getClass().getClassLoader(); private LanguageVersionDiscoverer languageVersionDiscoverer = new LanguageVersionDiscoverer(); + private LanguageVersion forceLanguageVersion; // Rule and source file options private List ruleSets; @@ -219,6 +220,35 @@ public class PMDConfiguration extends AbstractConfiguration { return languageVersionDiscoverer; } + /** + * Get the LanguageVersion specified by the force-language parameter. This overrides detection based on file + * extensions + * + * @return The LanguageVersion. + */ + public LanguageVersion getForceLanguageVersion() { + return forceLanguageVersion; + } + + /** + * Is the force-language parameter set to anything? + * + * @return true if ${@link #getForceLanguageVersion()} is not null + */ + public boolean isForceLanguageVersion() { + return forceLanguageVersion != null; + } + + /** + * Set the LanguageVersion specified by the force-language parameter. This overrides detection based on file + * extensions + * + * @param forceLanguageVersion the language version + */ + public void setForceLanguageVersion(LanguageVersion forceLanguageVersion) { + this.forceLanguageVersion = forceLanguageVersion; + } + /** * Set the given LanguageVersion as the current default for it's Language. * @@ -258,6 +288,13 @@ public class PMDConfiguration extends AbstractConfiguration { // Failure to determine the LanguageVersion for a file should be a hard // error, or simply cause the file to be skipped? public LanguageVersion getLanguageVersionOfFile(String fileName) { + LanguageVersion forcedVersion = getForceLanguageVersion(); + if (forcedVersion != null) { + // use force language if given + return forcedVersion; + } + + // otherwise determine by file extension LanguageVersion languageVersion = languageVersionDiscoverer.getDefaultLanguageVersionForFile(fileName); if (languageVersion == null) { // For compatibility with older code that does not always pass in diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java index 16f2b73f5e..2560080833 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java @@ -23,6 +23,10 @@ import net.sourceforge.pmd.util.document.FileLocation; * This forwards events to a {@link FileAnalysisListener}. It implements * violation suppression by filtering some violations out, according to * the {@link ViolationSuppressor}s for the language. + * + * A RuleContext contains a Rule instance and violation reporting methods + * implicitly report only for that rule. Contrary to PMD 6, RuleContext is + * not unique throughout the analysis, a separate one is used per file and rule. */ public final class RuleContext { // todo move to package reporting @@ -42,28 +46,71 @@ public final class RuleContext { this.rule = rule; } - // TODO document - private String getDefaultMessage() { return rule.getMessage(); } + /** + * Record a new violation of the contextual rule, at the given node. + * + * @param location Location of the violation + */ public void addViolation(Node location) { addViolationWithMessage(location, getDefaultMessage(), NO_ARGS); } + /** + * Record a new violation of the contextual rule, at the given node. + * The default violation message ({@link Rule#getMessage()}) is formatted + * using the given format arguments. + * + * @param location Location of the violation + * @param formatArgs Format arguments for the message + * + * @see MessageFormat + */ public void addViolation(Node location, Object... formatArgs) { addViolationWithMessage(location, getDefaultMessage(), formatArgs); } + /** + * Record a new violation of the contextual rule, at the given node. + * The given violation message ({@link Rule#getMessage()}) is treated + * as a format string for a {@link MessageFormat} and should hence use + * appropriate escapes. No formatting arguments are provided. + * + * @param location Location of the violation + * @param message Violation message + */ public void addViolationWithMessage(Node location, String message) { addViolationWithPosition(location, -1, -1, message, NO_ARGS); } + /** + * Record a new violation of the contextual rule, at the given node. + * The given violation message ({@link Rule#getMessage()}) is treated + * as a format string for a {@link MessageFormat} and should hence use + * appropriate escapes. The given formatting arguments are used. + * + * @param location Location of the violation + * @param message Violation message + * @param formatArgs Format arguments for the message + */ public void addViolationWithMessage(Node location, String message, Object... formatArgs) { addViolationWithPosition(location, -1, -1, message, formatArgs); } + /** + * Record a new violation of the contextual rule, at the given node. + * The position is refined using the given begin and end line numbers. + * The given violation message ({@link Rule#getMessage()}) is treated + * as a format string for a {@link MessageFormat} and should hence use + * appropriate escapes. The given formatting arguments are used. + * + * @param location Location of the violation + * @param message Violation message + * @param formatArgs Format arguments for the message + */ public void addViolationWithPosition(Node node, int beginLine, int endLine, String message, Object... formatArgs) { Objects.requireNonNull(node, "Node was null"); Objects.requireNonNull(message, "Message was null"); @@ -88,6 +135,12 @@ public final class RuleContext { } } + /** + * Force the recording of a violation, ignoring the violation + * suppression mechanism ({@link ViolationSuppressor}). + * + * @param rv A violation + */ public void addViolationNoSuppress(RuleViolation rv) { listener.onRuleViolation(rv); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index e3dbb3e702..635a2fdfb4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -31,6 +31,7 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; +import net.sourceforge.pmd.internal.DOMUtils; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.rules.RuleFactory; import net.sourceforge.pmd.util.ResourceLoader; @@ -156,7 +157,7 @@ final class RuleSetFactory { Node node = nodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { String nodeName = node.getNodeName(); - String text = parseTextNode(node); + String text = DOMUtils.parseTextNode(node); if (DESCRIPTION.equals(nodeName)) { ruleSetBuilder.withDescription(text); } else if ("include-pattern".equals(nodeName)) { @@ -302,7 +303,7 @@ final class RuleSetFactory { excludedRulesCheck.add(excludedRuleName); } } else if (isElementNode(child, PRIORITY)) { - priority = parseTextNode(child).trim(); + priority = DOMUtils.parseTextNode(child).trim(); } } final RuleSetReference ruleSetReference = new RuleSetReference(ref, true, excludedRulesCheck); @@ -532,32 +533,6 @@ final class RuleSetFactory { return node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(name); } - /** - * Parse a String from a textually type node. - * - * @param node - * The node. - * @return The String. - */ - private static String parseTextNode(Node node) { - - final int nodeCount = node.getChildNodes().getLength(); - if (nodeCount == 0) { - return ""; - } - - StringBuilder buffer = new StringBuilder(); - - for (int i = 0; i < nodeCount; i++) { - Node childNode = node.getChildNodes().item(i); - if (childNode.getNodeType() == Node.CDATA_SECTION_NODE || childNode.getNodeType() == Node.TEXT_NODE) { - buffer.append(childNode.getNodeValue()); - } - } - return buffer.toString(); - } - - /** * Determine if the specified rule element will represent a Rule with the * given name. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java index 3ed459d55c..cae151247c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java @@ -11,8 +11,6 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -22,6 +20,8 @@ import java.util.Properties; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.commons.lang3.reflect.MethodUtils; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.types.Parameter; @@ -202,13 +202,21 @@ public class Formatter { // in case of pipe or redirect, no interactive console. if (console != null) { try { - Field f = Console.class.getDeclaredField("cs"); - f.setAccessible(true); - Object res = f.get(console); + Object res = FieldUtils.readDeclaredField(console, "cs", true); if (res instanceof Charset) { return ((Charset) res).name(); } - } catch (ReflectiveOperationException ignored) { + } catch (IllegalArgumentException | ReflectiveOperationException ignored) { + // fall-through + } + + // Maybe this is Java17? Then there will be + // https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/Console.html#charset() + // instead of the field "cs". + try { + Charset charset = (Charset) MethodUtils.invokeMethod(console, "charset"); + return charset.name(); + } catch (IllegalArgumentException | ReflectiveOperationException ignored) { // fall-through } return getNativeConsoleEncoding(); @@ -218,13 +226,11 @@ public class Formatter { private static String getNativeConsoleEncoding() { try { - Method m = Console.class.getDeclaredMethod("encoding"); - m.setAccessible(true); - Object res = m.invoke(null); + Object res = MethodUtils.invokeStaticMethod(Console.class, "encoding"); if (res instanceof String) { return (String) res; } - } catch (ReflectiveOperationException ignored) { + } catch (IllegalArgumentException | ReflectiveOperationException ignored) { // fall-through } return null; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java index cb06d8fe16..ee87a4ccb1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java @@ -49,8 +49,8 @@ public abstract class AbstractAnalysisCache implements AnalysisCache { protected static final Logger LOG = Logger.getLogger(AbstractAnalysisCache.class.getName()); protected static final ClasspathFingerprinter FINGERPRINTER = new ClasspathFingerprinter(); protected final String pmdVersion; - protected final ConcurrentMap fileResultsCache; - protected final ConcurrentMap updatedResultsCache; + protected final ConcurrentMap fileResultsCache = new ConcurrentHashMap<>(); + protected final ConcurrentMap updatedResultsCache = new ConcurrentHashMap<>(); protected final CachedRuleMapper ruleMapper = new CachedRuleMapper(); protected long rulesetChecksum; protected long auxClassPathChecksum; @@ -61,8 +61,6 @@ public abstract class AbstractAnalysisCache implements AnalysisCache { */ public AbstractAnalysisCache() { pmdVersion = PMDVersion.VERSION; - fileResultsCache = new ConcurrentHashMap<>(); - updatedResultsCache = new ConcurrentHashMap<>(); } @Override @@ -215,12 +213,12 @@ public abstract class AbstractAnalysisCache implements AnalysisCache { @Override public FileAnalysisListener startFileAnalysis(TextFile filename) { - return new FileAnalysisListener() { - @Override - public void onRuleViolation(RuleViolation violation) { - final AnalysisResult analysisResult = updatedResultsCache.get(violation.getFilename()); + return violation -> { + final AnalysisResult analysisResult = + updatedResultsCache.get(violation.getFilename()); - analysisResult.addViolation(violation); // fixme this does NOT look thread-safe + synchronized (analysisResult) { + analysisResult.addViolation(violation); } }; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCache.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCache.java index 9c8c015782..e98c62a450 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCache.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisCache.java @@ -9,6 +9,7 @@ import java.util.List; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.reporting.FileAnalysisListener; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; import net.sourceforge.pmd.util.document.TextDocument; @@ -31,8 +32,8 @@ public interface AnalysisCache extends GlobalAnalysisListener { /** * Checks if a given file is up to date in the cache and can be skipped from analysis. * Regardless of the return value of this method, each call adds the parameter to the - * updated cache, which allows {@link #onRuleViolation(RuleViolation)} to add a rule - * violation to the file. TODO is this really best behaviour? This side-effects seems counter-intuitive. + * updated cache, which allows {@link FileAnalysisListener#onRuleViolation(RuleViolation)} + * to add a rule violation to the file. TODO is this really best behaviour? This side-effects seems counter-intuitive. * * @param document The file to check in the cache * @return True if the cache is a hit, false otherwise diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/FileAnalysisCache.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/FileAnalysisCache.java index 76a1cad3de..f3267d5d8e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/FileAnalysisCache.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/FileAnalysisCache.java @@ -153,7 +153,7 @@ public class FileAnalysisCache extends AbstractAnalysisCache { @Override public void close() throws Exception { - // todo + // nothing to do, PMD calls persist explicitly } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java index c3d4c81a71..1dc44417cb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java @@ -33,67 +33,67 @@ import com.beust.jcommander.validators.PositiveInteger; @InternalApi public class PMDParameters { - @Parameter(names = { "-rulesets", "-R" }, description = "Comma separated list of ruleset names to use.", + @Parameter(names = { "--rulesets", "-rulesets", "-R" }, description = "Comma separated list of ruleset names to use.", required = true) private String rulesets; - @Parameter(names = { "-uri", "-u" }, description = "Database URI for sources.") + @Parameter(names = { "--uri", "-uri", "-u" }, description = "Database URI for sources.") private String uri; - @Parameter(names = { "-dir", "-d" }, description = "Root directory for sources.") + @Parameter(names = { "--dir", "-dir", "-d" }, description = "Root directory for sources.") private String sourceDir; - @Parameter(names = "-filelist", description = "Path to a file containing a list of files to analyze.") + @Parameter(names = { "--file-list", "-filelist" }, description = "Path to a file containing a list of files to analyze.") private String fileListPath; - @Parameter(names = "-ignorelist", description = "Path to a file containing a list of files to ignore.") + @Parameter(names = { "--ignore-list", "-ignorelist" }, description = "Path to a file containing a list of files to ignore.") private String ignoreListPath; - @Parameter(names = { "-format", "-f" }, description = "Report format type.") + @Parameter(names = { "--format", "-format", "-f" }, description = "Report format type.") private String format = "text"; // Enhance to support other usage - @Parameter(names = { "-debug", "-verbose", "-D", "-V" }, description = "Debug mode.") + @Parameter(names = { "--debug", "--verbose", "-debug", "-verbose", "-D", "-V" }, description = "Debug mode.") private boolean debug = false; - @Parameter(names = { "-help", "-h", "-H" }, description = "Display help on usage.", help = true) + @Parameter(names = { "--help", "-help", "-h", "-H" }, description = "Display help on usage.", help = true) private boolean help = false; - @Parameter(names = { "-encoding", "-e" }, + @Parameter(names = { "--encoding", "-encoding", "-e" }, description = "Specifies the character set encoding of the source code files PMD is reading (i.e., UTF-8).") private String encoding = "UTF-8"; - @Parameter(names = { "-threads", "-t" }, description = "Sets the number of threads used by PMD.", + @Parameter(names = { "--threads", "-threads", "-t" }, description = "Sets the number of threads used by PMD.", validateWith = PositiveInteger.class) private int threads = 1; - @Parameter(names = { "-benchmark", "-b" }, + @Parameter(names = { "--benchmark", "-benchmark", "-b" }, description = "Benchmark mode - output a benchmark report upon completion; default to System.err.") private boolean benchmark = false; - @Parameter(names = { "-stress", "-S" }, description = "Performs a stress test.") + @Parameter(names = { "--stress", "-stress", "-S" }, description = "Performs a stress test.") private boolean stress = false; - @Parameter(names = "-shortnames", description = "Prints shortened filenames in the report.") + @Parameter(names = { "--short-names", "-shortnames" }, description = "Prints shortened filenames in the report.") private boolean shortnames = false; - @Parameter(names = "-showsuppressed", description = "Report should show suppressed rule violations.") + @Parameter(names = { "--show-suppressed", "-showsuppressed" }, description = "Report should show suppressed rule violations.") private boolean showsuppressed = false; - @Parameter(names = "-suppressmarker", + @Parameter(names = { "--suppress-marker", "-suppressmarker" }, description = "Specifies the string that marks a line which PMD should ignore; default is NOPMD.") private String suppressmarker = "NOPMD"; - @Parameter(names = { "-minimumpriority", "-min" }, + @Parameter(names = { "--minimum-priority", "-minimumpriority", "-min" }, description = "Rule priority threshold; rules with lower priority than configured here won't be used. " + "Valid values are integers between 1 and 5 (inclusive), with 5 being the lowest priority.", validateValueWith = RulePriorityValidator.class) private int minimumPriority = RulePriority.LOW.getPriority(); - @Parameter(names = { "-property", "-P" }, description = "{name}={value}: Define a property for the report format.", + @Parameter(names = { "--property", "-property", "-P" }, description = "{name}={value}: Define a property for the report format.", converter = PropertyConverter.class) private List properties = new ArrayList<>(); - @Parameter(names = { "-reportfile", "-r" }, + @Parameter(names = { "--report-file", "-reportfile", "-r" }, description = "Path to a file to which report output is written. " + "The file is created if it does not exist. " + "If this option is not specified, the report is rendered to standard output.") @@ -102,10 +102,16 @@ public class PMDParameters { @Parameter(names = { "-version", "-v" }, description = "Specify version of a language PMD should use.") private String version = null; + @Parameter(names = "--version", description = "Display current version of PMD and exit without performing any analysis.", help = true) + private boolean currentVersion = false; + @Parameter(names = { "-language", "-l" }, description = "Specify a language PMD should use.") private String language = null; - @Parameter(names = "-auxclasspath", + @Parameter(names = { "--force-language", "-force-language" }, description = "Force a language to be used for all input files, irrespective of filenames.") + private String forceLanguage = null; + + @Parameter(names = { "--aux-classpath", "-auxclasspath" }, description = "Specifies the classpath for libraries used by the source code. " + "This is used by the type resolution. The platform specific path delimiter " + "(\":\" on Linux, \";\" on Windows) is used to separate the entries. " @@ -113,22 +119,22 @@ public class PMDParameters { + "can be specified.") private String auxclasspath; - @Parameter(names = { "-failOnViolation", "--failOnViolation" }, arity = 1, + @Parameter(names = { "--fail-on-violation", "--failOnViolation", "-failOnViolation"}, arity = 1, description = "By default PMD exits with status 4 if violations are found. Disable this option with '-failOnViolation false' to exit with 0 instead and just write the report.") private boolean failOnViolation = true; - @Parameter(names = "-norulesetcompatibility", + @Parameter(names = { "--no-ruleset-compatibility", "-norulesetcompatibility" }, description = "Disable the ruleset compatibility filter. The filter is active by default and tries automatically 'fix' old ruleset files with old rule names") private boolean noRuleSetCompatibility = false; - @Parameter(names = "-cache", arity = 1, + @Parameter(names = { "--cache", "-cache" }, arity = 1, description = "Specify the location of the cache file for incremental analysis. " + "This should be the full path to the file, including the desired file name (not just the parent directory). " + "If the file doesn't exist, it will be created on the first run. The file will be overwritten on each run " + "with the most up-to-date rule violations.") private String cacheLocation = null; - @Parameter(names = "-no-cache", description = "Explicitly disable incremental analysis. The '-cache' option is ignored if this switch is present in the command line.") + @Parameter(names = { "--no-cache", "-no-cache" }, description = "Explicitly disable incremental analysis. The '-cache' option is ignored if this switch is present in the command line.") private boolean noCache = false; // this has to be a public static class, so that JCommander can use it! @@ -218,10 +224,16 @@ public class PMDParameters { configuration.setAnalysisCacheLocation(this.cacheLocation); configuration.setIgnoreIncrementalAnalysis(this.isIgnoreIncrementalAnalysis()); + LanguageVersion forceLangVersion = getForceLangVersion(); + if (forceLangVersion != null) { + configuration.setForceLanguageVersion(forceLangVersion); + } + LanguageVersion languageVersion = getLangVersion(); if (languageVersion != null) { configuration.getLanguageVersionDiscoverer().setDefaultLanguageVersion(languageVersion); } + try { configuration.prependClasspath(this.getAuxclasspath()); } catch (IOException e) { @@ -253,6 +265,10 @@ public class PMDParameters { return help; } + public boolean isVersion() { + return currentVersion; + } + public String getEncoding() { return encoding; } @@ -304,7 +320,7 @@ public class PMDParameters { return version != null ? lang.getVersion(version) : lang.getDefaultVersion(); } - + public String getVersion() { if (version != null) { return version; @@ -316,6 +332,15 @@ public class PMDParameters { return language != null ? language : LanguageRegistry.getDefaultLanguage().getTerseName(); } + private @Nullable LanguageVersion getForceLangVersion() { + Language lang = forceLanguage != null ? LanguageRegistry.findLanguageByTerseName(forceLanguage) : null; + return lang != null ? lang.getDefaultVersion() : null; + } + + public String getForceLanguage() { + return forceLanguage != null ? forceLanguage : ""; + } + public String getAuxclasspath() { return auxclasspath; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java index d6f8fe3aef..0ffc8465d0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PmdParametersParseResult.java @@ -4,6 +4,11 @@ package net.sourceforge.pmd.cli; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Objects; import net.sourceforge.pmd.PMDConfiguration; @@ -20,15 +25,19 @@ public final class PmdParametersParseResult { private final PMDParameters result; private final ParameterException error; + private final Map deprecatedOptionsUsed; - PmdParametersParseResult(PMDParameters result) { + PmdParametersParseResult(PMDParameters result, + Map deprecatedOptionsUsed) { this.result = Objects.requireNonNull(result); + this.deprecatedOptionsUsed = deprecatedOptionsUsed; this.error = null; } - PmdParametersParseResult(ParameterException error) { + PmdParametersParseResult(ParameterException error, Map deprecatedOptionsUsed) { this.result = null; this.error = Objects.requireNonNull(error); + this.deprecatedOptionsUsed = deprecatedOptionsUsed; } /** Returns true if parsing failed. */ @@ -44,6 +53,14 @@ public final class PmdParametersParseResult { return !isError() && result.isHelp(); } + /** + * Returns whether parsing just requested the {@code --version} text. + * In this case no configuration is produced. + */ + public boolean isVersion() { + return !isError() && result.isVersion(); + } + /** * Returns the error if parsing failed. Parsing may fail if required * parameters are not provided, or if some parameters don't pass validation. @@ -54,11 +71,20 @@ public final class PmdParametersParseResult { } /** - * Returns the resulting configuration if parsing succeeded and not {@link #isHelp(). + * Returns a map of deprecated CLI options used by the command that + * created this instance. Each key is a deprecated option that was used, + * and the value is a suggested replacement (a piece of English text). + */ + public Map getDeprecatedOptionsUsed() { + return deprecatedOptionsUsed; + } + + /** + * Returns the resulting configuration if parsing succeeded and neither {@link #isHelp()} nor {@link #isVersion()} is requested. * Otherwise returns null. */ public PMDConfiguration toConfiguration() { - return result != null && !isHelp() ? result.toConfiguration() : null; + return result != null && !isHelp() && !isVersion() ? result.toConfiguration() : null; } /** @@ -80,9 +106,50 @@ public final class PmdParametersParseResult { try { jcommander.parse(args); - return new PmdParametersParseResult(result); + return new PmdParametersParseResult(result, filterDeprecatedOptions(args)); } catch (ParameterException e) { - return new PmdParametersParseResult(e); + return new PmdParametersParseResult(e, filterDeprecatedOptions(args)); } } + + private static Map filterDeprecatedOptions(String... args) { + Map argSet = new LinkedHashMap<>(SUGGESTED_REPLACEMENT); + argSet.keySet().retainAll(new HashSet<>(Arrays.asList(args))); + return Collections.unmodifiableMap(argSet); + } + + /** Map of deprecated option to suggested replacement. */ + private static final Map SUGGESTED_REPLACEMENT; + + static { + Map m = new LinkedHashMap<>(); + + m.put("-rulesets", "--rulesets (or -R)"); + m.put("-uri", "--uri"); + m.put("-dir", "--dir (or -d)"); + m.put("-filelist", "--file-list"); + m.put("-ignorelist", "--ignore-list"); + m.put("-format", "--format (or -f)"); + m.put("-debug", "--debug"); + m.put("-verbose", "--verbose"); + m.put("-help", "--help"); + m.put("-encoding", "--encoding"); + m.put("-threads", "--threads"); + m.put("-benchmark", "--benchmark"); + m.put("-stress", "--stress"); + m.put("-shortnames", "--short-names"); + m.put("-showsuppressed", "--show-suppressed"); + m.put("-suppressmarker", "--suppress-marker"); + m.put("-minimumpriority", "--minimum-priority"); + m.put("-property", "--property"); + m.put("-reportfile", "--report-file"); + m.put("-force-language", "--force-language"); + m.put("-auxclasspath", "--aux-classpath"); + m.put("-failOnViolation", "--fail-on-violation"); + m.put("--failOnViolation", "--fail-on-violation"); + m.put("-norulesetcompatibility", "--no-ruleset-compatibility"); + m.put("-cache", "--cache"); + m.put("-no-cache", "--no-cache"); + SUGGESTED_REPLACEMENT = Collections.unmodifiableMap(m); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java index 2aae2302e5..a18b4d2791 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java @@ -147,13 +147,12 @@ public class CPD { } private void addAndSkipLexicalErrors(SourceCode sourceCode) throws IOException { - TokenEntry.State savedTokenEntry = new TokenEntry.State(tokens.getTokens()); + final TokenEntry.State savedState = new TokenEntry.State(); try { addAndThrowLexicalError(sourceCode); } catch (TokenMgrError e) { System.err.println("Skipping " + sourceCode.getFileName() + ". Reason: " + e.getMessage()); - tokens.getTokens().clear(); - tokens.getTokens().addAll(savedTokenEntry.restore()); + savedState.restore(tokens); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java index f0083d4d75..0bda3cb30b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java @@ -13,7 +13,12 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; @@ -80,6 +85,14 @@ public final class CPDCommandLineInterface { setStatusCodeOrExit(ERROR_STATUS); return; } + + Map deprecatedOptions = filterDeprecatedOptions(args); + if (!deprecatedOptions.isEmpty()) { + Entry first = deprecatedOptions.entrySet().iterator().next(); + LOGGER.warning("Some deprecated options were used on the command-line, including " + first.getKey()); + LOGGER.warning("Consider replacing it with " + first.getValue()); + } + arguments.postContruct(); // Pass extra parameters as System properties to allow language // implementation to retrieve their associate values... @@ -111,6 +124,24 @@ public final class CPDCommandLineInterface { } } + private static Map filterDeprecatedOptions(String... args) { + Map argSet = new LinkedHashMap<>(SUGGESTED_REPLACEMENT); + argSet.keySet().retainAll(new HashSet<>(Arrays.asList(args))); + return Collections.unmodifiableMap(argSet); + } + + /** Map of deprecated option to suggested replacement. */ + private static final Map SUGGESTED_REPLACEMENT; + + static { + Map m = new LinkedHashMap<>(); + + m.put("--failOnViolation", "--fail-on-violation"); + m.put("-failOnViolation", "--fail-on-violation"); + m.put("--filelist", "--file-list"); + SUGGESTED_REPLACEMENT = Collections.unmodifiableMap(m); + } + public static void addSourceFilesToCPD(CPD cpd, CPDConfiguration arguments) { // Add files if (null != arguments.getFiles() && !arguments.getFiles().isEmpty()) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 262b0e5096..6a56ec7ff0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -114,7 +114,7 @@ public class CPDConfiguration extends AbstractConfiguration { required = false, converter = FileConverter.class) private List files; - @Parameter(names = "--filelist", description = "Path to a file containing a list of files to analyze.", + @Parameter(names = { "--filelist", "--file-list" }, description = "Path to a file containing a list of files to analyze.", required = false) private String fileListPath; @@ -131,7 +131,7 @@ public class CPDConfiguration extends AbstractConfiguration { @Parameter(names = { "--help", "-h" }, description = "Print help text", required = false, help = true) private boolean help; - @Parameter(names = { "--failOnViolation", "-failOnViolation" }, arity = 1, + @Parameter(names = { "--fail-on-violation", "--failOnViolation", "-failOnViolation" }, arity = 1, description = "By default CPD exits with status 4 if code duplications are found. Disable this option with '-failOnViolation false' to exit with 0 instead and just write the report.") private boolean failOnViolation = true; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java index b72b984110..c28113416a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java @@ -1,75 +1,53 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.cpd; -import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Properties; -import java.util.ServiceConfigurationError; -import java.util.ServiceLoader; -public final class LanguageFactory { +import net.sourceforge.pmd.internal.LanguageServiceBase; + +public final class LanguageFactory extends LanguageServiceBase { public static final String EXTENSION = "extension"; public static final String BY_EXTENSION = "by_extension"; - private static LanguageFactory instance = new LanguageFactory(); + private static final Comparator LANGUAGE_COMPARATOR = new Comparator() { + @Override + public int compare(Language o1, Language o2) { + return o1.getTerseName().compareToIgnoreCase(o2.getTerseName()); + } + }; + + private static final NameExtractor NAME_EXTRACTOR = new NameExtractor() { + @Override + public String getName(Language language) { + return language.getName().toLowerCase(Locale.ROOT); + } + }; + + private static final NameExtractor TERSE_NAME_EXTRACTOR = new NameExtractor() { + @Override + public String getName(Language language) { + return language.getTerseName().toLowerCase(Locale.ROOT); + } + }; + + // Important: the "instance" needs to be defined *after* LANGUAGE_COMPARATOR and *NAME_EXTRACTOR + // as these are needed in the constructor. + private static final LanguageFactory INSTANCE = new LanguageFactory(); public static String[] supportedLanguages; static { - supportedLanguages = instance.languages.keySet().toArray(new String[instance.languages.size()]); + supportedLanguages = INSTANCE.languagesByTerseName.keySet().toArray(new String[INSTANCE.languages.size()]); } - private Map languages = new HashMap<>(); - private LanguageFactory() { - List languagesList = new ArrayList<>(); - // Use current class' classloader instead of the threads context classloader, see https://github.com/pmd/pmd/issues/1788 - ServiceLoader languageLoader = ServiceLoader.load(Language.class, getClass().getClassLoader()); - Iterator iterator = languageLoader.iterator(); - - while (true) { - // this loop is weird, but both hasNext and next may throw ServiceConfigurationError, - // it's more robust that way - try { - if (iterator.hasNext()) { - Language language = iterator.next(); - languagesList.add(language); - } else { - break; - } - } catch (UnsupportedClassVersionError | ServiceConfigurationError e) { - // Some languages require java8 and are therefore only available - // if java8 or later is used as runtime. - System.err.println("Ignoring language for PMD: " + e.toString()); - } - } - - // sort languages by terse name. Avoiding differences in the order of languages - // across JVM versions / OS. - Collections.sort(languagesList, new Comparator() { - @Override - public int compare(Language o1, Language o2) { - return o1.getTerseName().compareToIgnoreCase(o2.getTerseName()); - } - }); - - // using a linked hash map to maintain insertion order - languages = new LinkedHashMap<>(); - for (Language language : languagesList) { - languages.put(language.getTerseName().toLowerCase(Locale.ROOT), language); - } - + super(Language.class, LANGUAGE_COMPARATOR, NAME_EXTRACTOR, TERSE_NAME_EXTRACTOR); } public static Language createLanguage(String language) { @@ -79,9 +57,9 @@ public final class LanguageFactory { public static Language createLanguage(String language, Properties properties) { Language implementation; if (BY_EXTENSION.equals(language)) { - implementation = instance.getLanguageByExtension(properties.getProperty(EXTENSION)); + implementation = INSTANCE.getLanguageByExtension(properties.getProperty(EXTENSION)); } else { - implementation = instance.languages.get(instance.languageAliases(language).toLowerCase(Locale.ROOT)); + implementation = INSTANCE.languagesByTerseName.get(INSTANCE.languageAliases(language).toLowerCase(Locale.ROOT)); } if (implementation == null) { // No proper implementation @@ -103,7 +81,7 @@ public final class LanguageFactory { private Language getLanguageByExtension(String extension) { Language result = null; - for (Language language : languages.values()) { + for (Language language : languages) { if (language.getExtensions().contains(extension)) { result = language; break; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java index 25edfa5ef6..72e19f3404 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java @@ -4,12 +4,13 @@ package net.sourceforge.pmd.cpd; -import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.util.document.FileLocation; public class TokenEntry implements Comparable { @@ -98,23 +99,30 @@ public class TokenEntry implements Comparable { /** * Helper class to preserve and restore the current state of the token * entries. + * + * @deprecated This is internal API. */ + @InternalApi + @Deprecated public static class State { - private int tokenCount; - private Map tokens; - private List entries; + private final int tokenCount; + private final int tokensMapSize; - public State(List entries) { + public State() { this.tokenCount = TokenEntry.TOKEN_COUNT.get().intValue(); - this.tokens = new HashMap<>(TokenEntry.TOKENS.get()); - this.entries = new ArrayList<>(entries); + this.tokensMapSize = TokenEntry.TOKENS.get().size(); } - public List restore() { + public void restore(Tokens tokens) { + final List entries = tokens.getTokens(); TokenEntry.TOKEN_COUNT.get().set(tokenCount); - TOKENS.get().clear(); - TOKENS.get().putAll(tokens); - return entries; + final Iterator> it = TOKENS.get().entrySet().iterator(); + while (it.hasNext()) { + if (it.next().getValue() > tokensMapSize) { + it.remove(); + } + } + entries.subList(tokenCount, entries.size()).clear(); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index 0058787240..0840613dcc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -43,5 +43,4 @@ public class Tokens { public List getTokens() { return tokens; } - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/DOMUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/DOMUtils.java new file mode 100644 index 0000000000..b28059f4d5 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/DOMUtils.java @@ -0,0 +1,40 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.internal; + +import org.w3c.dom.Node; + +import net.sourceforge.pmd.annotation.InternalApi; + +@InternalApi +public final class DOMUtils { + private DOMUtils() { + // utility + } + + /** + * Parse a String from a textually type node. + * + * @param node The node. + * + * @return The String. + */ + public static String parseTextNode(Node node) { + final int nodeCount = node.getChildNodes().getLength(); + if (nodeCount == 0) { + return ""; + } + + StringBuilder buffer = new StringBuilder(); + + for (int i = 0; i < nodeCount; i++) { + Node childNode = node.getChildNodes().item(i); + if (childNode.getNodeType() == Node.CDATA_SECTION_NODE || childNode.getNodeType() == Node.TEXT_NODE) { + buffer.append(childNode.getNodeValue()); + } + } + return buffer.toString(); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/LanguageServiceBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/LanguageServiceBase.java new file mode 100644 index 0000000000..096cbbe603 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/LanguageServiceBase.java @@ -0,0 +1,68 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.internal; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.TreeSet; + +import net.sourceforge.pmd.annotation.InternalApi; + +@InternalApi +public abstract class LanguageServiceBase { + + protected interface NameExtractor { + String getName(T language); + } + + protected final Set languages; + protected final Map languagesByName; + protected final Map languagesByTerseName; + + protected LanguageServiceBase(final Class serviceType, final Comparator comparator, + final NameExtractor nameExtractor, final NameExtractor terseNameExtractor) { + Set sortedLangs = new TreeSet<>(comparator); + // Use current class' classloader instead of the threads context classloader, see https://github.com/pmd/pmd/issues/1788 + ServiceLoader languageLoader = ServiceLoader.load(serviceType, getClass().getClassLoader()); + Iterator iterator = languageLoader.iterator(); + + while (true) { + // this loop is weird, but both hasNext and next may throw ServiceConfigurationError, + // it's more robust that way + try { + if (iterator.hasNext()) { + T language = iterator.next(); + sortedLangs.add(language); + } else { + break; + } + } catch (UnsupportedClassVersionError | ServiceConfigurationError e) { + // Some languages require java8 and are therefore only available + // if java8 or later is used as runtime. + System.err.println("Ignoring language for PMD: " + e.toString()); + } + } + + // using a linked hash map to maintain insertion order + languages = Collections.unmodifiableSet(new LinkedHashSet<>(sortedLangs)); + + // TODO there may be languages with duplicate names + Map byName = new LinkedHashMap<>(); + Map byTerseName = new LinkedHashMap<>(); + for (T language : sortedLangs) { + byName.put(nameExtractor.getName(language), language); + byTerseName.put(terseNameExtractor.getName(language), language); + } + languagesByName = Collections.unmodifiableMap(byName); + languagesByTerseName = Collections.unmodifiableMap(byTerseName); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ContextedAssertionError.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ContextedAssertionError.java index ee856350d8..e4ab1a5bdf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ContextedAssertionError.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ContextedAssertionError.java @@ -4,19 +4,16 @@ package net.sourceforge.pmd.internal.util; -import java.util.List; -import java.util.Set; - import org.apache.commons.lang3.exception.DefaultExceptionContext; -import org.apache.commons.lang3.exception.ExceptionContext; -import org.apache.commons.lang3.tuple.Pair; /** * An {@link AssertionError} with nice messages. */ -public final class ContextedAssertionError extends AssertionError implements ExceptionContext { +public final class ContextedAssertionError extends AssertionError implements ExceptionContextDefaultImpl { + /** The serialization version. */ + private static final long serialVersionUID = -8919808081157463410L; - private final ExceptionContext exceptionContext = new DefaultExceptionContext(); + private final DefaultExceptionContext exceptionContext = new DefaultExceptionContext(); private ContextedAssertionError(AssertionError e) { super(e.getMessage()); @@ -35,39 +32,12 @@ public final class ContextedAssertionError extends AssertionError implements Exc } @Override - public ContextedAssertionError addContextValue(String label, Object value) { - exceptionContext.addContextValue(label, value); + public DefaultExceptionContext getExceptionContext() { + return exceptionContext; + } + + @Override + public ContextedAssertionError getThrowable() { return this; } - - @Override - public ContextedAssertionError setContextValue(String label, Object value) { - exceptionContext.addContextValue(label, value); - return this; - } - - @Override - public List getContextValues(String label) { - return exceptionContext.getContextValues(label); - } - - @Override - public Object getFirstContextValue(String label) { - return exceptionContext.getFirstContextValue(label); - } - - @Override - public Set getContextLabels() { - return exceptionContext.getContextLabels(); - } - - @Override - public List> getContextEntries() { - return exceptionContext.getContextEntries(); - } - - @Override - public String getFormattedExceptionMessage(String baseMessage) { - return exceptionContext.getFormattedExceptionMessage(baseMessage); - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ContextedStackOverflowError.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ContextedStackOverflowError.java index d771d537f5..e2fa9b2fe0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ContextedStackOverflowError.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ContextedStackOverflowError.java @@ -4,19 +4,16 @@ package net.sourceforge.pmd.internal.util; -import java.util.List; -import java.util.Set; - import org.apache.commons.lang3.exception.DefaultExceptionContext; -import org.apache.commons.lang3.exception.ExceptionContext; -import org.apache.commons.lang3.tuple.Pair; /** * A {@link StackOverflowError} with nice messages. */ -public final class ContextedStackOverflowError extends StackOverflowError implements ExceptionContext { +public final class ContextedStackOverflowError extends StackOverflowError implements ExceptionContextDefaultImpl { + /** The serialization version. */ + private static final long serialVersionUID = 4111035582093848670L; - private final ExceptionContext exceptionContext = new DefaultExceptionContext(); + private final DefaultExceptionContext exceptionContext = new DefaultExceptionContext(); private ContextedStackOverflowError(StackOverflowError e) { super(e.getMessage()); @@ -35,39 +32,12 @@ public final class ContextedStackOverflowError extends StackOverflowError implem } @Override - public ContextedStackOverflowError addContextValue(String label, Object value) { - exceptionContext.addContextValue(label, value); + public DefaultExceptionContext getExceptionContext() { + return exceptionContext; + } + + @Override + public ContextedStackOverflowError getThrowable() { return this; } - - @Override - public ContextedStackOverflowError setContextValue(String label, Object value) { - exceptionContext.addContextValue(label, value); - return this; - } - - @Override - public List getContextValues(String label) { - return exceptionContext.getContextValues(label); - } - - @Override - public Object getFirstContextValue(String label) { - return exceptionContext.getFirstContextValue(label); - } - - @Override - public Set getContextLabels() { - return exceptionContext.getContextLabels(); - } - - @Override - public List> getContextEntries() { - return exceptionContext.getContextEntries(); - } - - @Override - public String getFormattedExceptionMessage(String baseMessage) { - return exceptionContext.getFormattedExceptionMessage(baseMessage); - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ExceptionContextDefaultImpl.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ExceptionContextDefaultImpl.java new file mode 100644 index 0000000000..00b16df120 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/ExceptionContextDefaultImpl.java @@ -0,0 +1,54 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.internal.util; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.exception.ExceptionContext; +import org.apache.commons.lang3.tuple.Pair; + +public interface ExceptionContextDefaultImpl extends ExceptionContext { + + ExceptionContext getExceptionContext(); + + T getThrowable(); + + @Override + default T addContextValue(String label, Object value) { + getExceptionContext().addContextValue(label, value); + return getThrowable(); + } + + @Override + default ExceptionContext setContextValue(String label, Object value) { + return getExceptionContext().addContextValue(label, value); + } + + @Override + default List getContextValues(String label) { + return getExceptionContext().getContextValues(label); + } + + @Override + default Object getFirstContextValue(String label) { + return getExceptionContext().getFirstContextValue(label); + } + + @Override + default Set getContextLabels() { + return getExceptionContext().getContextLabels(); + } + + @Override + default List> getContextEntries() { + return getExceptionContext().getContextEntries(); + } + + @Override + default String getFormattedExceptionMessage(String baseMessage) { + return getExceptionContext().getFormattedExceptionMessage(baseMessage); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java index ce577ae118..73859c2547 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -6,66 +6,47 @@ package net.sourceforge.pmd.lang; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; +import java.util.Comparator; import java.util.List; -import java.util.Map; -import java.util.ServiceConfigurationError; -import java.util.ServiceLoader; import java.util.Set; -import java.util.TreeSet; + +import net.sourceforge.pmd.internal.LanguageServiceBase; /** * Provides access to the registered PMD languages. These are found * from the classpath of the {@link ClassLoader} of this class. */ -public final class LanguageRegistry { +public final class LanguageRegistry extends LanguageServiceBase { + // sort languages by name. Avoiding differences in the order of languages + // across JVM versions / OS. + private static final Comparator LANGUAGE_COMPARATOR = new Comparator() { + @Override + public int compare(Language o1, Language o2) { + return o1.getTerseName().compareToIgnoreCase(o2.getTerseName()); + } + }; + + private static final NameExtractor NAME_EXTRACTOR = new NameExtractor() { + @Override + public String getName(Language language) { + return language.getName(); + } + }; + + private static final NameExtractor TERSE_NAME_EXTRACTOR = new NameExtractor() { + @Override + public String getName(Language language) { + return language.getTerseName(); + } + }; + + // Important: the INSTANCE needs to be defined *after* LANGUAGE_COMPARATOR and *NAME_EXTRACTOR + // as these are needed in the constructor. private static final LanguageRegistry INSTANCE = new LanguageRegistry(); - private final Map languagesByName; - private final Map languagesByTerseName; - private final Set languages; - private LanguageRegistry() { - // sort languages by terse name. Avoiding differences in the order of languages - // across JVM versions / OS. - Set sortedLangs = new TreeSet<>((o1, o2) -> o1.getTerseName().compareToIgnoreCase(o2.getTerseName())); - // Use current class' classloader instead of the threads context classloader, see https://github.com/pmd/pmd/issues/1377 - ServiceLoader languageLoader = ServiceLoader.load(Language.class, getClass().getClassLoader()); - Iterator iterator = languageLoader.iterator(); - - while (true) { - // this loop is weird, but both hasNext and next may throw ServiceConfigurationError, - // it's more robust that way - try { - if (iterator.hasNext()) { - Language language = iterator.next(); - sortedLangs.add(language); - } else { - break; - } - } catch (UnsupportedClassVersionError | ServiceConfigurationError e) { - // Some languages require java8 and are therefore only available - // if java8 or later is used as runtime. - System.err.println("Ignoring language for PMD: " + e.toString()); - } - } - - languages = Collections.unmodifiableSet(new LinkedHashSet<>(sortedLangs)); - - // using a linked hash map to maintain insertion order - // TODO there may be languages with duplicate names - Map byName = new LinkedHashMap<>(); - Map byTerseName = new LinkedHashMap<>(); - for (Language language : sortedLangs) { - byName.put(language.getName(), language); - byTerseName.put(language.getTerseName(), language); - } - languagesByName = Collections.unmodifiableMap(byName); - languagesByTerseName = Collections.unmodifiableMap(byTerseName); + super(Language.class, LANGUAGE_COMPARATOR, NAME_EXTRACTOR, TERSE_NAME_EXTRACTOR); } /** @@ -81,7 +62,7 @@ public final class LanguageRegistry { * is by terse name. */ public static Set getLanguages() { - return getInstance().languages; + return INSTANCE.languages; } /** @@ -93,7 +74,7 @@ public final class LanguageRegistry { * @return A language, or null if the name is unknown */ public static Language getLanguage(String languageName) { - return getInstance().languagesByName.get(languageName); + return INSTANCE.languagesByName.get(languageName); } /** @@ -123,7 +104,7 @@ public final class LanguageRegistry { * @return A language, or null if the name is unknown */ public static Language findLanguageByTerseName(String terseName) { - return getInstance().languagesByTerseName.get(terseName); + return INSTANCE.languagesByTerseName.get(terseName); } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java index 625f654af0..94bf68af88 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersion.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang; +import java.util.List; + import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.annotation.InternalApi; @@ -120,27 +122,10 @@ public class LanguageVersion implements Comparable { @Override public int compareTo(LanguageVersion o) { - if (o == null) { - return 1; - } - - int comp = getName().compareTo(o.getName()); - if (comp != 0) { - return comp; - } - - String[] vals1 = getName().split("\\."); - String[] vals2 = o.getName().split("\\."); - int i = 0; - while (i < vals1.length && i < vals2.length && vals1[i].equals(vals2[i])) { - i++; - } - if (i < vals1.length && i < vals2.length) { - int diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i])); - return Integer.signum(diff); - } else { - return Integer.signum(vals1.length - vals2.length); - } + List versions = language.getVersions(); + int thisPosition = versions.indexOf(this); + int otherPosition = versions.indexOf(o); + return Integer.compare(thisPosition, otherPosition); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java index d12c91f76c..75b2f90b3f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java @@ -14,6 +14,7 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.ToIntFunction; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -620,6 +621,23 @@ public interface NodeStream<@NonNull T extends Node> extends Iterable<@NonNull T return result; } + /** + * Sum the elements of this stream by associating them to an integer. + * + * @param toInt Map an element to an integer, which will be added + * to the running sum + * returns the next intermediate result + * + * @return The sum, zero if the stream is empty. + */ + default int sumBy(ToIntFunction toInt) { + int result = 0; + for (T node : this) { + result += toInt.applyAsInt(node); + } + return result; + } + /** * Returns the number of nodes in this stream. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java index 566836ae0b..f610a15f75 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java @@ -278,21 +278,21 @@ public abstract class AbstractRule extends AbstractPropertySource implements Rul // TODO remove those methods, make Rules have type-safe access to a RuleContext /** - * @see RuleContext#addViolation(Rule, Node, Object...) + * @see RuleContext#addViolation(Node) */ public void addViolation(Object data, Node node) { ((RuleContext) data).addViolation(node); } /** - * @see RuleContext#addViolation(Rule, Node, Object...) + * @see RuleContext#addViolation(Node, Object[]) */ public void addViolation(Object data, Node node, Object... args) { ((RuleContext) data).addViolation(node, args); } /** - * @see RuleContext#addViolationWithMessage(Rule, Node, String, Object...) + * @see RuleContext#addViolationWithMessage(Node, String) */ public void addViolationWithMessage(Object data, Node node, String message) { ((RuleContext) data).addViolationWithMessage(node, message); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AbstractXPathFunctionDef.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AbstractXPathFunctionDef.java index 47a2d86815..7a466ea25c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AbstractXPathFunctionDef.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AbstractXPathFunctionDef.java @@ -19,12 +19,18 @@ public abstract class AbstractXPathFunctionDef extends ExtensionFunctionDefiniti private static final String PMD_URI_PREFIX = "http://pmd.sourceforge.net/"; private final StructuredQName qname; - protected AbstractXPathFunctionDef(String localName, String languageTerseName) { - String namespacePrefix = "pmd-" + languageTerseName; - String uri = PMD_URI_PREFIX + namespacePrefix; + private AbstractXPathFunctionDef(String localName, String namespacePrefix, String uri) { this.qname = new StructuredQName(namespacePrefix, uri, localName); } + protected AbstractXPathFunctionDef(String localName) { + this(localName, "pmd", PMD_URI_PREFIX + "pmd-core"); + } + + protected AbstractXPathFunctionDef(String localName, String languageTerseName) { + this(localName, "pmd-" + languageTerseName, PMD_URI_PREFIX + "pmd-" + languageTerseName); + } + @Override public final StructuredQName getFunctionQName() { return qname; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/XPathHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/XPathHandler.java index 3e048f3adb..7ca9d5893c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/XPathHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/XPathHandler.java @@ -5,8 +5,10 @@ package net.sourceforge.pmd.lang.rule.xpath.impl; import java.util.Collections; +import java.util.HashSet; import java.util.Set; +import net.sourceforge.pmd.lang.rule.xpath.internal.DefaultXPathFunctions; import net.sourceforge.pmd.util.CollectionUtil; import net.sf.saxon.lib.ExtensionFunctionDefinition; @@ -26,15 +28,16 @@ public interface XPathHandler { static XPathHandler noFunctionDefinitions() { - return Collections::emptySet; + return () -> DefaultXPathFunctions.getDefaultFunctions(); } - /** * Returns a default XPath handler. */ static XPathHandler getHandlerForFunctionDefs(ExtensionFunctionDefinition first, ExtensionFunctionDefinition... defs) { - Set set = CollectionUtil.setOf(first, defs); - return () -> set; + Set set = new HashSet<>(CollectionUtil.setOf(first, defs)); + set.addAll(DefaultXPathFunctions.getDefaultFunctions()); + + return () -> Collections.unmodifiableSet(set); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/DefaultXPathFunctions.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/DefaultXPathFunctions.java new file mode 100644 index 0000000000..bf0a074f70 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/DefaultXPathFunctions.java @@ -0,0 +1,26 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + + +package net.sourceforge.pmd.lang.rule.xpath.internal; + +import java.util.Set; + +import net.sourceforge.pmd.util.CollectionUtil; + +import net.sf.saxon.lib.ExtensionFunctionDefinition; + +/** + * Default XPath functions provided by pmd-core. + */ +public final class DefaultXPathFunctions { + + private DefaultXPathFunctions() { + // utility class + } + + public static Set getDefaultFunctions() { + return CollectionUtil.setOf(FileNameXPathFunction.INSTANCE); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/FileNameXPathFunction.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/FileNameXPathFunction.java new file mode 100644 index 0000000000..1d0a1dffea --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/FileNameXPathFunction.java @@ -0,0 +1,62 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.rule.xpath.internal; + +import java.nio.file.Paths; +import java.util.Objects; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.RootNode; +import net.sourceforge.pmd.lang.rule.xpath.impl.AbstractXPathFunctionDef; + +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.ExtensionFunctionCall; +import net.sf.saxon.om.Sequence; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.value.SequenceType; +import net.sf.saxon.value.StringValue; + +/** + * A function that returns the current file name. + * + * @author Clément Fournier + */ +public final class FileNameXPathFunction extends AbstractXPathFunctionDef { + + public static final FileNameXPathFunction INSTANCE = new FileNameXPathFunction(); + + private FileNameXPathFunction() { + super("fileName"); + } + + @Override + public SequenceType[] getArgumentTypes() { + return new SequenceType[0]; + } + + @Override + public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { + return SequenceType.STRING_SEQUENCE; + } + + @Override + public ExtensionFunctionCall makeCallExpression() { + return new ExtensionFunctionCall() { + + @Override + public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { + Node node = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); + RootNode root = node.getRoot(); + Objects.requireNonNull(root, "No root node in tree?"); + + String fileName = root.getTextDocument().getDisplayName(); + Objects.requireNonNull(fileName, "File name was not set"); + String simpleFilename = Paths.get(fileName).getFileName().toString(); + + return new StringValue(simpleFilename); + } + }; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/RuleChainAnalyzer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/RuleChainAnalyzer.java index fe04a55145..afaf60ff6b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/RuleChainAnalyzer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/RuleChainAnalyzer.java @@ -6,8 +6,10 @@ package net.sourceforge.pmd.lang.rule.xpath.internal; import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import java.util.ArrayDeque; import java.util.Collections; import java.util.Comparator; +import java.util.Deque; import java.util.List; import net.sourceforge.pmd.lang.ast.Node; @@ -22,6 +24,7 @@ import net.sf.saxon.expr.SlashExpression; import net.sf.saxon.expr.VennExpression; import net.sf.saxon.expr.sort.DocumentSorter; import net.sf.saxon.om.AxisInfo; +import net.sf.saxon.pattern.CombinedNodeTest; import net.sf.saxon.pattern.NameTest; import net.sf.saxon.type.Type; @@ -45,6 +48,7 @@ public class RuleChainAnalyzer extends SaxonExprVisitor { private boolean rootElementReplaced; private boolean insideExpensiveExpr; private boolean foundPathInsideExpensive; + private boolean foundCombinedNodeTest; public RuleChainAnalyzer(Configuration currentConfiguration) { this.configuration = currentConfiguration; @@ -92,7 +96,17 @@ public class RuleChainAnalyzer extends SaxonExprVisitor { Expression step = newPath.getStep(); if (step instanceof FilterExpression) { FilterExpression filterExpression = (FilterExpression) step; - result = new FilterExpression(new AxisExpression(AxisInfo.SELF, null), filterExpression.getFilter()); + + Deque filters = new ArrayDeque<>(); + Expression walker = filterExpression; + while (walker instanceof FilterExpression) { + filters.push(((FilterExpression) walker).getFilter()); + walker = ((FilterExpression) walker).getBase(); + } + result = new FilterExpression(new AxisExpression(AxisInfo.SELF, null), filters.pop()); + while (!filters.isEmpty()) { + result = new FilterExpression(result, filters.pop()); + } rootElementReplaced = true; } else if (step instanceof AxisExpression) { Expression start = newPath.getStart(); @@ -124,13 +138,15 @@ public class RuleChainAnalyzer extends SaxonExprVisitor { @Override public Expression visit(AxisExpression e) { - if (rootElement == null && e.getNodeTest() instanceof NameTest) { + if (rootElement == null && e.getNodeTest() instanceof NameTest && !foundCombinedNodeTest) { NameTest test = (NameTest) e.getNodeTest(); if (test.getPrimitiveType() == Type.ELEMENT && e.getAxis() == AxisInfo.DESCENDANT) { rootElement = listOf(configuration.getNamePool().getClarkName(test.getFingerprint())); } else if (test.getPrimitiveType() == Type.ELEMENT && e.getAxis() == AxisInfo.CHILD) { rootElement = listOf(configuration.getNamePool().getClarkName(test.getFingerprint())); } + } else if (e.getNodeTest() instanceof CombinedNodeTest) { + foundCombinedNodeTest = true; } return super.visit(e); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java index d7e0ca7ef0..f386f94921 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java @@ -154,7 +154,7 @@ public abstract class PropertyDescriptorBuilderConversionWrapper Element type of the list * @param Concrete type of the underlying builder @@ -223,6 +223,7 @@ public abstract class PropertyDescriptorBuilderConversionWrapper fields) { super.populate(builder, fields); @@ -230,6 +231,7 @@ public abstract class PropertyDescriptorBuilderConversionWrapper files, PMDConfiguration configuration, Set languages) throws IOException { List ignoredFiles = getIgnoredFiles(configuration); - Predicate fileFilter = PredicateUtil.toFileFilter(new LanguageFilenameFilter(languages)); + LanguageVersion forcedVersion = configuration.getForceLanguageVersion(); + Predicate fileFilter = + forcedVersion != null ? Files::isRegularFile // accept everything except dirs + : PredicateUtil.toFileFilter(new LanguageFilenameFilter(languages)); fileFilter = fileFilter.and(path -> !ignoredFiles.contains(path.toString())); for (String root : configuration.getAllInputPaths()) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java index 82d755db58..cc59031cb7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java @@ -79,7 +79,7 @@ public final class IOUtil { if (StringUtils.isBlank(reportFile)) { return createWriter(); } - Path path = new File(reportFile).toPath(); + Path path = new File(reportFile).toPath().toAbsolutePath(); Files.createDirectories(path.getParent()); // ensure parent dir exists // this will create the file if it doesn't exist return Files.newBufferedWriter(path, getDefaultCharset()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/document/TextFileBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/document/TextFileBuilder.java index 41cc9c7d2d..1f341c01ee 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/document/TextFileBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/document/TextFileBuilder.java @@ -15,6 +15,7 @@ import net.sourceforge.pmd.lang.LanguageVersion; /** * A builder for a new text file. + * See static methods on {@link TextFile}. */ @SuppressWarnings("PMD.MissingStaticMethodInNonInstantiatableClass") public abstract class TextFileBuilder { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/AntLogHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/AntLogHandler.java index c995a5c4f6..9484e407af 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/AntLogHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/AntLogHandler.java @@ -50,6 +50,7 @@ public class AntLogHandler extends Handler { this.project = project; } + @SuppressWarnings("PMD.AvoidAccessibilityAlteration") public Level getAntLogLevel() { for (final BuildListener l : project.getBuildListeners()) { Field declaredField = null; @@ -129,6 +130,7 @@ public class AntLogHandler extends Handler { // nothing to do } + @SuppressWarnings("PMD.AvoidAccessibilityAlteration") private Level determineGradleLogLevel(BuildListener l) { try { project.log("Detected gradle AntLoggingAdapter", Project.MSG_DEBUG); diff --git a/pmd-core/src/main/resources/rulesets/internal/all-java.xml b/pmd-core/src/main/resources/rulesets/internal/all-java.xml index 569b015ced..a8f260812f 100644 --- a/pmd-core/src/main/resources/rulesets/internal/all-java.xml +++ b/pmd-core/src/main/resources/rulesets/internal/all-java.xml @@ -23,6 +23,9 @@ .*/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java10/LocalVariableTypeInference_varAsEnumName.java .*/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java10/LocalVariableTypeInference_varAsTypeIdentifier.java + + .*/net/sourceforge/pmd/lang/java/ast/InfiniteLoopInLookahead.java + diff --git a/pmd-core/src/main/resources/rulesets/releases/34.xml b/pmd-core/src/main/resources/rulesets/releases/34.xml index a3328515ae..6d2924711e 100644 --- a/pmd-core/src/main/resources/rulesets/releases/34.xml +++ b/pmd-core/src/main/resources/rulesets/releases/34.xml @@ -15,7 +15,7 @@ This ruleset contains links to rules that are new in PMD v3.4 - + diff --git a/pmd-core/src/main/resources/rulesets/releases/35.xml b/pmd-core/src/main/resources/rulesets/releases/35.xml index 7753b454c6..debffe92c2 100644 --- a/pmd-core/src/main/resources/rulesets/releases/35.xml +++ b/pmd-core/src/main/resources/rulesets/releases/35.xml @@ -16,9 +16,9 @@ This ruleset contains links to rules that are new in PMD v3.5 - + - + diff --git a/pmd-core/src/main/resources/rulesets/releases/36.xml b/pmd-core/src/main/resources/rulesets/releases/36.xml index 16a32f41f4..8a5cdf594f 100644 --- a/pmd-core/src/main/resources/rulesets/releases/36.xml +++ b/pmd-core/src/main/resources/rulesets/releases/36.xml @@ -11,7 +11,7 @@ This ruleset contains links to rules that are new in PMD v3.6 - + diff --git a/pmd-core/src/main/resources/rulesets/releases/38.xml b/pmd-core/src/main/resources/rulesets/releases/38.xml index b0528c8caa..9dacc12123 100644 --- a/pmd-core/src/main/resources/rulesets/releases/38.xml +++ b/pmd-core/src/main/resources/rulesets/releases/38.xml @@ -10,7 +10,7 @@ This ruleset contains links to rules that are new in PMD v3.8 - + diff --git a/pmd-core/src/main/resources/rulesets/releases/40rc1.xml b/pmd-core/src/main/resources/rulesets/releases/40rc1.xml index 132f866adf..0ef4736820 100644 --- a/pmd-core/src/main/resources/rulesets/releases/40rc1.xml +++ b/pmd-core/src/main/resources/rulesets/releases/40rc1.xml @@ -8,9 +8,9 @@ This ruleset contains links to rules that are new in PMD v4.0rc1 - - - + + + @@ -26,4 +26,3 @@ This ruleset contains links to rules that are new in PMD v4.0rc1 - \ No newline at end of file diff --git a/pmd-core/src/main/resources/rulesets/releases/41.xml b/pmd-core/src/main/resources/rulesets/releases/41.xml index 031efb5aee..cbd7252332 100644 --- a/pmd-core/src/main/resources/rulesets/releases/41.xml +++ b/pmd-core/src/main/resources/rulesets/releases/41.xml @@ -13,7 +13,7 @@ This ruleset contains links to rules that are new in PMD v4.1 - + diff --git a/pmd-core/src/main/resources/rulesets/releases/42.xml b/pmd-core/src/main/resources/rulesets/releases/42.xml index 01fe98bc02..aa5f42de36 100644 --- a/pmd-core/src/main/resources/rulesets/releases/42.xml +++ b/pmd-core/src/main/resources/rulesets/releases/42.xml @@ -11,7 +11,7 @@ This ruleset contains links to rules that are new in PMD v4.2 - + diff --git a/pmd-core/src/main/resources/rulesets/releases/50.xml b/pmd-core/src/main/resources/rulesets/releases/50.xml index d612ceac00..7406926431 100644 --- a/pmd-core/src/main/resources/rulesets/releases/50.xml +++ b/pmd-core/src/main/resources/rulesets/releases/50.xml @@ -38,7 +38,7 @@ This ruleset contains links to rules that are new in PMD v5.0 - + diff --git a/pmd-core/src/main/resources/rulesets/releases/550.xml b/pmd-core/src/main/resources/rulesets/releases/550.xml index 5fb17c056b..82a29edb22 100644 --- a/pmd-core/src/main/resources/rulesets/releases/550.xml +++ b/pmd-core/src/main/resources/rulesets/releases/550.xml @@ -9,7 +9,7 @@ This ruleset contains links to rules that are new in PMD v5.5.0 - + diff --git a/pmd-core/src/main/resources/rulesets/releases/6370.xml b/pmd-core/src/main/resources/rulesets/releases/6370.xml new file mode 100644 index 0000000000..2f712034f1 --- /dev/null +++ b/pmd-core/src/main/resources/rulesets/releases/6370.xml @@ -0,0 +1,15 @@ + + + + +This ruleset contains links to rules that are new in PMD v6.37.0 + + + + + + + diff --git a/pmd-core/src/main/resources/rulesets/releases/6400.xml b/pmd-core/src/main/resources/rulesets/releases/6400.xml new file mode 100644 index 0000000000..a2b5bf9e31 --- /dev/null +++ b/pmd-core/src/main/resources/rulesets/releases/6400.xml @@ -0,0 +1,13 @@ + + + + +This ruleset contains links to rules that are new in PMD v6.40.0 + + + + + diff --git a/pmd-core/src/main/resources/rulesets/releases/700.xml b/pmd-core/src/main/resources/rulesets/releases/700.xml index 2b99b84cd5..75821d4a05 100644 --- a/pmd-core/src/main/resources/rulesets/releases/700.xml +++ b/pmd-core/src/main/resources/rulesets/releases/700.xml @@ -10,5 +10,7 @@ This ruleset contains links to rules that are new in PMD v7.0.0 + + diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java index bcf485503e..c9674af4fc 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java @@ -5,31 +5,18 @@ package net.sourceforge.pmd; import java.util.function.BiConsumer; -import java.util.function.Consumer; import org.junit.Assert; import org.junit.Test; -import net.sourceforge.pmd.Report.ReportBuilderListener; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.ast.impl.DummyTreeUtil; -import net.sourceforge.pmd.reporting.FileAnalysisListener; public class RuleContextTest { - public static Report getReport(Consumer sideEffects) { - ReportBuilderListener listener = new ReportBuilderListener(); - try { - sideEffects.accept(listener); - } finally { - listener.close(); - } - return listener.getResult(); - } - public static Report getReport(Rule rule, BiConsumer sideEffects) throws Exception { - return getReport(listener -> sideEffects.accept(rule, RuleContext.create(listener, rule))); + return Report.buildReport(listener -> sideEffects.accept(rule, RuleContext.create(listener, rule))); } public static Report getReportForRuleApply(Rule rule, Node node) throws Exception { @@ -37,7 +24,7 @@ public class RuleContextTest { } public static Report getReportForRuleSetApply(RuleSet ruleset, RootNode node) throws Exception { - return getReport(listener -> new RuleSets(ruleset).apply(node, listener)); + return Report.buildReport(listener -> new RuleSets(ruleset).apply(node, listener)); } @Test diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java index 5be5ef73cd..8fc9cef3ac 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -423,7 +423,7 @@ public class RuleSetTest { RuleSets ruleSets = new RuleSets(listOf(ruleSet1, ruleSet2)); // Two violations - Report report = RuleContextTest.getReport(ctx -> ruleSets.apply(makeCompilationUnits(), ctx)); + Report report = Report.buildReport(ctx1 -> ruleSets.apply(makeCompilationUnits(), ctx1)); assertEquals("Violations", 2, report.getViolations().size()); // One violation @@ -434,7 +434,7 @@ public class RuleSetTest { RuleSets ruleSets2 = new RuleSets(listOf(ruleSet1, ruleSet2)); - report = RuleContextTest.getReport(ctx -> ruleSets2.apply(makeCompilationUnits("C:\\package\\RandomClass.java"), ctx)); + report = Report.buildReport(ctx -> ruleSets2.apply(makeCompilationUnits("C:\\package\\RandomClass.java"), ctx)); assertEquals("Violations", 1, report.getViolations().size()); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java index a3bba10c4d..6437b7a8b7 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java @@ -12,8 +12,10 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.logging.Logger; import org.junit.Before; import org.junit.Rule; @@ -22,6 +24,7 @@ import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.TemporaryFolder; import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.junit.JavaUtilLoggingRule; /** * @@ -35,6 +38,9 @@ public class CoreCliTest { public TemporaryFolder tempDir = new TemporaryFolder(); @Rule public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); + @Rule + public JavaUtilLoggingRule loggingRule = new JavaUtilLoggingRule(PMD.class.getPackage().getName()).mute(); + private Path srcDir; @Before @@ -46,6 +52,8 @@ public class CoreCliTest { // create a few files srcDir = Files.createDirectories(root.resolve("src")); writeString(srcDir.resolve("someSource.dummy"), "dummy text"); + + Logger.getLogger("net.sourceforge.pmd"); } @@ -58,24 +66,102 @@ public class CoreCliTest { assertTrue("Report file should exist", Files.exists(reportFile)); - runPmdSuccessfully("-d", srcDir, "-R", DUMMY_RULESET, "-r", reportFile); + runPmdSuccessfully("-no-cache", "-d", srcDir, "-R", DUMMY_RULESET, "-r", reportFile); assertNotEquals(readString(reportFile), STRING_TO_REPLACE); } @Test - public void testNonExistentReportFile() { + public void testPreExistingReportFileLongOption() throws IOException { + Path reportFile = tempRoot().resolve("out/reportFile.txt"); + // now we create the file + Files.createDirectories(reportFile.getParent()); + writeString(reportFile, STRING_TO_REPLACE); + + assertTrue("Report file should exist", Files.exists(reportFile)); + + runPmdSuccessfully("--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET, "--report-file", reportFile); + + assertNotEquals("Report file should have been overwritten", readString(reportFile), STRING_TO_REPLACE); + } + + @Test + public void testNonExistentReportFile() throws IOException { Path reportFile = tempRoot().resolve("out/reportFile.txt"); assertFalse("Report file should not exist", Files.exists(reportFile)); - runPmdSuccessfully("-d", srcDir, "-R", DUMMY_RULESET, "-r", reportFile); + try { + runPmdSuccessfully("-no-cache", "-d", srcDir, "-R", DUMMY_RULESET, "-r", reportFile); + assertTrue("Report file should have been created", Files.exists(reportFile)); + } finally { + Files.deleteIfExists(reportFile); + } + } + + @Test + public void testNonExistentReportFileLongOption() { + Path reportFile = tempRoot().resolve("out/reportFile.txt"); + + assertFalse("Report file should not exist", Files.exists(reportFile)); + + runPmdSuccessfully("--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET, "--report-file", reportFile); assertTrue("Report file should have been created", Files.exists(reportFile)); } + @Test + public void testNonExistentReportFileDeprecatedOptions() { + Path reportFile = tempRoot().resolve("out/reportFile.txt"); + assertFalse("Report file should not exist", Files.exists(reportFile)); + runPmdSuccessfully("-no-cache", "-dir", srcDir, "-rulesets", DUMMY_RULESET, "-reportfile", reportFile); + + assertTrue("Report file should have been created", Files.exists(reportFile)); + assertTrue(loggingRule.getLog().contains("Some deprecated options were used on the command-line, including -rulesets")); + assertTrue(loggingRule.getLog().contains("Consider replacing it with --rulesets (or -R)")); + // only one parameter is logged + assertFalse(loggingRule.getLog().contains("Some deprecated options were used on the command-line, including -reportfile")); + assertFalse(loggingRule.getLog().contains("Consider replacing it with --report-file")); + } + + /** + * This tests to create the report file in the current working directory. + * + *

Note: We can't change the cwd in the running VM, so the file will not be created + * in the temporary folder, but really in the cwd. The test fails if a file already exists + * and makes sure to cleanup the file afterwards. + */ + @Test + public void testRelativeReportFile() throws IOException { + String reportFile = "reportFile.txt"; + Path absoluteReportFile = FileSystems.getDefault().getPath(reportFile).toAbsolutePath(); + // verify the file doesn't exist yet - we will delete the file at the end! + assertFalse("Report file must not exist yet!", Files.exists(absoluteReportFile)); + + try { + runPmdSuccessfully("-no-cache", "-d", srcDir, "-R", DUMMY_RULESET, "-r", reportFile); + assertTrue("Report file should have been created", Files.exists(absoluteReportFile)); + } finally { + Files.deleteIfExists(absoluteReportFile); + } + } + + @Test + public void testRelativeReportFileLongOption() throws IOException { + String reportFile = "reportFile.txt"; + Path absoluteReportFile = FileSystems.getDefault().getPath(reportFile).toAbsolutePath(); + // verify the file doesn't exist yet - we will delete the file at the end! + assertFalse("Report file must not exist yet!", Files.exists(absoluteReportFile)); + + try { + runPmdSuccessfully("--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET, "--report-file", reportFile); + assertTrue("Report file should have been created", Files.exists(absoluteReportFile)); + } finally { + Files.deleteIfExists(absoluteReportFile); + } + } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java index 432e96d623..5923717395 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDCommandLineInterfaceTest.java @@ -67,6 +67,18 @@ public class PMDCommandLineInterfaceTest { assertTrue(config.getAnalysisCache() instanceof NoopAnalysisCache); } + @Test + public void testNoCacheSwitchLongOption() { + PMDParameters params = new PMDParameters(); + String[] args = {"-d", "source_folder", "-f", "ideaj", "-R", "java-empty", "--cache", "/home/user/.pmd/cache", "--no-cache", }; + PMDCommandLineInterface.extractParameters(params, args, "PMD"); + + assertTrue(params.isIgnoreIncrementalAnalysis()); + PMDConfiguration config = params.toConfiguration(); + assertTrue(config.isIgnoreIncrementalAnalysis()); + assertTrue(config.getAnalysisCache() instanceof NoopAnalysisCache); + } + @Test public void testSetStatusCodeOrExitDoExit() { exit.expectSystemExitWithStatus(0); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index acc80b8704..2eda04c9c6 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -4,24 +4,66 @@ package net.sourceforge.pmd.cpd; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Arrays; + import org.junit.Assert; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.contrib.java.lang.system.SystemOutRule; +import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; +import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.junit.JavaUtilLoggingRule; + public class CPDCommandLineInterfaceTest { + private static final String SRC_DIR = "src/test/resources/net/sourceforge/pmd/cpd/files/"; + @Rule public final TestRule restoreSystemProperties = new RestoreSystemProperties(); @Rule - public final SystemOutRule log = new SystemOutRule().enableLog(); + public final SystemOutRule log = new SystemOutRule().enableLog().muteForSuccessfulTests(); + @Rule + public final JavaUtilLoggingRule loggingRule = new JavaUtilLoggingRule(PMD.class.getPackage().getName()).mute(); + @Rule + public TemporaryFolder tempDir = new TemporaryFolder(); + + @Before + public void setup() { + System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true"); + } + @Test public void testEmptyResultRendering() { - System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true"); CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--files", - "src/test/resources/net/sourceforge/pmd/cpd/files/", "--format", "xml", }); + SRC_DIR, "--format", "xml", }); Assert.assertEquals("" + "\n" + "", log.getLog().trim()); } + + @Test + public void testDeprecatedOptionsWarning() throws IOException { + File filelist = new File(tempDir.getRoot(), "cpd-test-file-list.txt"); + Files.write(filelist.toPath(), Arrays.asList( + new File(SRC_DIR, "dup1.java").getAbsolutePath(), + new File(SRC_DIR, "dup2.java").getAbsolutePath()), StandardCharsets.UTF_8); + + CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--filelist", + filelist.getAbsolutePath(), "--format", "xml", "-failOnViolation", "true" }); + Assert.assertEquals("" + "\n" + "", log.getLog().trim()); + assertTrue(loggingRule.getLog().contains("Some deprecated options were used on the command-line, including -failOnViolation")); + assertTrue(loggingRule.getLog().contains("Consider replacing it with --fail-on-violation")); + // only one parameter is logged + assertFalse(loggingRule.getLog().contains("Some deprecated options were used on the command-line, including --filelist")); + assertFalse(loggingRule.getLog().contains("Consider replacing it with --file-list")); + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/FileReporterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/FileReporterTest.java index f5e94f8fbc..69768f7ecb 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/FileReporterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/FileReporterTest.java @@ -7,10 +7,10 @@ package net.sourceforge.pmd.cpd; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.io.Reader; import org.apache.commons.io.IOUtils; import org.junit.Test; @@ -55,7 +55,7 @@ public class FileReporterTest { } private String readFile(File file) throws IOException { - try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + try (Reader reader = new FileReader(file)) { String text = IOUtils.toString(reader); return text.replaceAll("\\R", "\n"); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/junit/JavaUtilLoggingRule.java b/pmd-core/src/test/java/net/sourceforge/pmd/junit/JavaUtilLoggingRule.java index b779aeeb4e..572ed0388f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/junit/JavaUtilLoggingRule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/junit/JavaUtilLoggingRule.java @@ -21,6 +21,7 @@ public class JavaUtilLoggingRule extends ExternalResource { private final Logger logger; private final ByteArrayOutputStream stream; private final StreamHandler customLogHandler; + private boolean mute; /** @@ -39,13 +40,20 @@ public class JavaUtilLoggingRule extends ExternalResource { this.customLogHandler = new StreamHandler(stream, currentLogger.getHandlers()[0].getFormatter()); } + public JavaUtilLoggingRule mute() { + this.mute = true; + return this; + } + @Override protected void before() throws Throwable { + logger.setUseParentHandlers(!mute); logger.addHandler(customLogHandler); } @Override protected void after() { + logger.setUseParentHandlers(true); logger.removeHandler(customLogHandler); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageParameterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageParameterTest.java new file mode 100644 index 0000000000..97dbfd9a23 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageParameterTest.java @@ -0,0 +1,26 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang; + +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.cli.PMDCommandLineInterface; +import net.sourceforge.pmd.cli.PMDParameters; + +public class LanguageParameterTest { + + /** Test that language parameters from the CLI are correctly passed through to the PMDConfiguration. Although this is a + * CLI test, it resides here to take advantage of {@link net.sourceforge.pmd.lang.DummyLanguageModule} + */ + @Test + public void testLanguageFromCliToConfiguration() { + PMDParameters params = new PMDParameters(); + String[] args = { "-d", "source_folder", "-f", "ideaj", "-P", "sourcePath=/home/user/source/", "-R", "java-empty", "-force-language", "dummy"}; + PMDCommandLineInterface.extractParameters(params, args, "PMD"); + + Assert.assertEquals(new DummyLanguageModule().getDefaultVersion().getName(), params.toConfiguration().getForceLanguageVersion().getName()); + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQueryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQueryTest.java index 454ccc12a5..2a1a70aa43 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQueryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQueryTest.java @@ -322,6 +322,61 @@ public class SaxonXPathRuleQueryTest { assertExpression("docOrder((((/)/descendant::element(Q{}dummyNode))[matches(convertUntyped(data(@SimpleName)), \"a\", \"\")])/child::element(Q{}foo))", query.getFallbackExpr()); } + @Test + public void ruleChainVisitWithTwoFunctions() { + SaxonXPathRuleQuery query = createQuery("//dummyNode[ends-with(@Image, 'foo')][pmd-dummy:imageIs('bar')]"); + List ruleChainVisits = query.getRuleChainVisits(); + Assert.assertEquals(1, ruleChainVisits.size()); + Assert.assertTrue(ruleChainVisits.contains("dummyNode")); + Assert.assertEquals(2, query.nodeNameToXPaths.size()); + assertExpression("let $v0 := imageIs(\"bar\") return ((self::node()[ends-with(convertUntyped(data(@Image)), \"foo\")])[$v0])", query.nodeNameToXPaths.get("dummyNode").get(0)); + } + + @Test + public void ruleChainWithUnions() { + SaxonXPathRuleQuery query = createQuery("(//ForStatement | //WhileStatement | //DoStatement)//AssignmentOperator"); + List ruleChainVisits = query.getRuleChainVisits(); + Assert.assertEquals(0, ruleChainVisits.size()); + } + + @Test + public void ruleChainWithUnionsAndFilter() { + SaxonXPathRuleQuery query = createQuery("(//ForStatement | //WhileStatement | //DoStatement)//AssignmentOperator[@Image='foo']"); + List ruleChainVisits = query.getRuleChainVisits(); + Assert.assertEquals(0, ruleChainVisits.size()); + } + + @Test + public void ruleChainWithUnionsCustomFunctionsVariant1() { + SaxonXPathRuleQuery query = createQuery("(//ForStatement | //WhileStatement | //DoStatement)//dummyNode[pmd-dummy:imageIs(@Image)]"); + List ruleChainVisits = query.getRuleChainVisits(); + Assert.assertEquals(0, ruleChainVisits.size()); + } + + @Test + public void ruleChainWithUnionsCustomFunctionsVariant2() { + SaxonXPathRuleQuery query = createQuery("//(ForStatement | WhileStatement | DoStatement)//dummyNode[pmd-dummy:imageIs(@Image)]"); + List ruleChainVisits = query.getRuleChainVisits(); + Assert.assertEquals(0, ruleChainVisits.size()); + } + + @Test + public void ruleChainWithUnionsCustomFunctionsVariant3() { + SaxonXPathRuleQuery query = createQuery("//ForStatement//dummyNode[pmd-dummy:imageIs(@Image)]" + + " | //WhileStatement//dummyNode[pmd-dummy:imageIs(@Image)]" + + " | //DoStatement//dummyNode[pmd-dummy:imageIs(@Image)]"); + List ruleChainVisits = query.getRuleChainVisits(); + Assert.assertEquals(3, ruleChainVisits.size()); + Assert.assertTrue(ruleChainVisits.contains("ForStatement")); + Assert.assertTrue(ruleChainVisits.contains("WhileStatement")); + Assert.assertTrue(ruleChainVisits.contains("DoStatement")); + + final String expectedSubexpression = "(self::node()/descendant::element(dummyNode))[imageIs(exactly-one(convertUntyped(data(@Image))))]"; + assertExpression(expectedSubexpression, query.nodeNameToXPaths.get("ForStatement").get(0)); + assertExpression(expectedSubexpression, query.nodeNameToXPaths.get("WhileStatement").get(0)); + assertExpression(expectedSubexpression, query.nodeNameToXPaths.get("DoStatement").get(0)); + } + private static void assertExpression(String expected, Expression actual) { assertEquals(normalizeExprDump(expected), normalizeExprDump(actual.toString())); diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 1663f9d031..fa7387f8ab 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -77,6 +77,37 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-ant + pre-integration-test + + copy + + + + + + org.apache.ant + ant + 1.9.16 + + + org.apache.ant + ant-launcher + 1.9.16 + + + ${project.build.directory}/ant + true + + + + org.apache.maven.plugins maven-failsafe-plugin @@ -252,7 +283,7 @@ org.apache.commons commons-compress - 1.19 + 1.21 test @@ -289,5 +320,36 @@ + + jdk17-compat-it + + + java17.home + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + jdk17-compat-it + + integration-test + verify + + + + ${java17.home} + ${java17.home}/bin:${env.PATH} + + + + + + + + diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AbstractBinaryDistributionTest.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AbstractBinaryDistributionTest.java index 2822d2867a..d45bcd7cd6 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AbstractBinaryDistributionTest.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AbstractBinaryDistributionTest.java @@ -14,9 +14,10 @@ import org.junit.rules.TemporaryFolder; import net.sourceforge.pmd.PMDVersion; public abstract class AbstractBinaryDistributionTest { + public static final String PMD_BIN_PREFIX = "pmd-bin-"; protected static File getBinaryDistribution() { - return new File(".", "target/pmd-bin-" + PMDVersion.VERSION + ".zip"); + return new File(".", "target/" + PMD_BIN_PREFIX + PMDVersion.VERSION + ".zip"); } @ClassRule diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java new file mode 100644 index 0000000000..6d11b7d9d7 --- /dev/null +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java @@ -0,0 +1,81 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.it; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.SystemUtils; +import org.junit.Assume; +import org.junit.Test; + +import net.sourceforge.pmd.PMDVersion; + +/** + * This test calls ant in a fake terminal to make sure we have a {@link java.io.Console} connected. + * This however only works under linux. + *

+ * See How to trick an application into thinking its stdout is a terminal, not a pipe. + */ +public class AntIT extends AbstractBinaryDistributionTest { + + @Test + public void runAnt() throws IOException, InterruptedException { + Assume.assumeTrue(SystemUtils.IS_OS_LINUX); + + String antBasepath = new File("target/ant").getAbsolutePath(); + String pmdHome = tempDir.resolve(PMD_BIN_PREFIX + PMDVersion.VERSION).toAbsolutePath().toString(); + File antTestProjectFolder = prepareAntTestProjectFolder(); + + ExecutionResult result = runAnt(antBasepath, pmdHome, antTestProjectFolder); + result.assertExecutionResult(0, "BUILD SUCCESSFUL"); + result.assertExecutionResult(0, "NoPackage"); // the no package rule + } + + + private File prepareAntTestProjectFolder() throws IOException { + File sourceProjectFolder = new File("src/test/resources/ant-it"); + File projectFolder = folder.newFolder(); + FileUtils.copyDirectory(sourceProjectFolder, projectFolder); + return projectFolder; + } + + + private ExecutionResult runAnt(String antLibPath, String pmdHomePath, File antTestProjectFolder) + throws IOException, InterruptedException { + String cmd = System.getenv("JAVA_HOME") + "/bin/java" + " -cp \"" + antLibPath + "/*\"" + + " -jar " + antLibPath + "/ant-launcher.jar -Dpmd.home=" + pmdHomePath; + + // https://stackoverflow.com/questions/1401002/how-to-trick-an-application-into-thinking-its-stdout-is-a-terminal-not-a-pipe/20401674#20401674 + ProcessBuilder pb = new ProcessBuilder("script", "-qfec", cmd, "/dev/null"); + pb.directory(antTestProjectFolder); + pb.redirectErrorStream(true); + + final ExecutionResult.Builder result = new ExecutionResult.Builder(); + final Process process = pb.start(); + Thread outputReader = new Thread(new Runnable() { + @Override + public void run() { + try (InputStream in = process.getInputStream()) { + String output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); + result.withOutput(output); + } catch (IOException e) { + result.withOutput("Exception occurred: " + e.toString()); + } + } + }); + outputReader.start(); + int exitCode = process.waitFor(); + outputReader.join(TimeUnit.SECONDS.toMillis(5)); + + result.withExitCode(exitCode); + return result.build(); + } +} diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java index 40a04e8ef6..921d61a75b 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java @@ -23,7 +23,6 @@ import net.sourceforge.pmd.PMDVersion; * @author Andreas Dangel */ public class PMDExecutor { - private static final String PMD_BIN_PREFIX = "pmd-bin-"; private static final String SOURCE_DIRECTORY_FLAG = "-d"; private static final String RULESET_FLAG = "-R"; private static final String FORMAT_FLAG = "-f"; @@ -35,7 +34,7 @@ public class PMDExecutor { } private static ExecutionResult runPMDUnix(Path tempDir, Path reportFile, String ... arguments) throws Exception { - String cmd = tempDir.resolve(PMD_BIN_PREFIX + PMDVersion.VERSION + "/bin/run.sh").toAbsolutePath().toString(); + String cmd = tempDir.resolve(AbstractBinaryDistributionTest.PMD_BIN_PREFIX + PMDVersion.VERSION + "/bin/run.sh").toAbsolutePath().toString(); List args = new ArrayList<>(); args.add("pmd"); args.addAll(Arrays.asList(arguments)); @@ -43,7 +42,7 @@ public class PMDExecutor { } private static ExecutionResult runPMDWindows(Path tempDir, Path reportFile, String ... arguments) throws Exception { - String cmd = tempDir.resolve(PMD_BIN_PREFIX + PMDVersion.VERSION + "/bin/pmd.bat").toAbsolutePath().toString(); + String cmd = tempDir.resolve(AbstractBinaryDistributionTest.PMD_BIN_PREFIX + PMDVersion.VERSION + "/bin/pmd.bat").toAbsolutePath().toString(); return runPMD(cmd, Arrays.asList(arguments), reportFile); } diff --git a/pmd-dist/src/test/resources/ant-it/build.xml b/pmd-dist/src/test/resources/ant-it/build.xml new file mode 100644 index 0000000000..edb7cf84b4 --- /dev/null +++ b/pmd-dist/src/test/resources/ant-it/build.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + rulesets/java/quickstart.xml + + + + + + + diff --git a/pmd-dist/src/test/resources/ant-it/src/Sample.java b/pmd-dist/src/test/resources/ant-it/src/Sample.java new file mode 100644 index 0000000000..0dd08d397a --- /dev/null +++ b/pmd-dist/src/test/resources/ant-it/src/Sample.java @@ -0,0 +1 @@ +public class Sample {} diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java index f6c0ae2383..750e7c934c 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java @@ -35,6 +35,7 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.io.IOUtils; @@ -284,8 +285,8 @@ public class DeadLinksChecker { private List listMdFiles(Path pagesDirectory) { - try { - return Files.walk(pagesDirectory) + try (Stream stream = Files.walk(pagesDirectory)) { + return stream .filter(Files::isRegularFile) .filter(path -> path.toString().endsWith(".md")) .collect(Collectors.toList()); diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 683837a393..605e9528fe 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -1,4 +1,9 @@ /** + * Promote "JEP 409: Sealed Classes" as permanent language feature with Java 17. + * Support "JEP 406: Pattern Matching for switch (Preview)" for Java 17 Preview. + * Remove support for Java 15 preview language features + * Andreas Dangel 07/2021 + *==================================================================== * Fix #3117 - infinite loop when parsing invalid code nested in lambdas * Andreas Dangel 03/2021 *==================================================================== @@ -298,7 +303,7 @@ class JavaParserImpl { } private boolean isSealedClassSupported() { - return jdkVersion == 15 && preview || jdkVersion == 16 && preview; + return jdkVersion == 16 && preview || jdkVersion >= 17; } /** @@ -311,6 +316,7 @@ class JavaParserImpl { */ private boolean inSwitchLabel = false; + // This is a semantic LOOKAHEAD to determine if we're dealing with an assert // Note that this can't be replaced with a syntactic lookahead // since "assert" isn't a string literal token @@ -371,7 +377,7 @@ class JavaParserImpl { return false; } - private boolean classModifierLookahead() { + private boolean classModifierForLocalTypesLookahead() { Token next = getToken(1); return next.kind == AT || next.kind == PUBLIC @@ -380,9 +386,7 @@ class JavaParserImpl { || next.kind == ABSTRACT || next.kind == STATIC || next.kind == FINAL - || next.kind == STRICTFP - || isSealedClassSupported() && isKeyword("sealed") - || isSealedClassSupported() && isNonSealedModifier(); + || next.kind == STRICTFP; } private boolean localTypeDeclAfterModifiers() { @@ -397,9 +401,7 @@ class JavaParserImpl { } private boolean localTypeDeclGivenNextIsIdent() { - return localTypesSupported() && ( - isNonSealedModifier() || isRecordStart() || isEnumStart() - ); + return localTypesSupported() && (isRecordStart() || isEnumStart()); } /** @@ -1714,6 +1716,31 @@ void EqualityExpression() #void: )* } +void Pattern() #void: +{} +{ + PrimaryPattern() [ GuardedPatternCondition() #GuardedPattern(2) ] +} + +void GuardedPatternCondition() #void: +{} +{ + "&&" ConditionalAndExpression() +} + +void PrimaryPattern() #void: +{} +{ + TypePattern() + | "(" Pattern() ")" { AstImplUtil.bumpParenDepth((ASTPattern) jjtree.peekNode()); } +} + +void TypePattern(): +{} +{ + LocalVarModifierList() ReferenceType() VariableDeclaratorId() +} + void InstanceOfExpression() #void: {} { @@ -1721,9 +1748,11 @@ void InstanceOfExpression() #void: LOOKAHEAD(1) ("instanceof" ( - AnnotatedRefType() [ VariableDeclaratorId() #TypePattern(2) ] + AnnotatedRefType() [ VariableDeclaratorId() #TypePattern(2) ] | - ( LocalVarModifierList() ReferenceType() VariableDeclaratorId() ) #TypePattern(3) + LOOKAHEAD("final" | "@") PrimaryPattern() + | + LOOKAHEAD("(") Pattern() ) { jjtThis.setOp(BinaryOp.INSTANCEOF); @@ -2405,13 +2434,23 @@ void SwitchLabel() : { { inSwitchLabel = true; } ( - "case" ConditionalExpression() ( "," ConditionalExpression() )* + "case" CaseLabelElement(jjtThis) ( "," CaseLabelElement(jjtThis) )* | "default" {jjtThis.setDefault();} ) { inSwitchLabel = false; } } +void CaseLabelElement(ASTSwitchLabel label) #void: +{} +{ + "default" {label.setDefault();} + | + LOOKAHEAD(Pattern()) Pattern() + | + ConditionalExpression() +} + void YieldStatement() : { } { diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index bd5a99cdbf..00d515d6e3 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -267,5 +267,11 @@ 3.11.0 test + + commons-logging + commons-logging + 1.2 + test + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java index 23253815b5..a9d257552d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java @@ -30,9 +30,10 @@ public class JavaLanguageModule extends BaseLanguageModule { addVersion("13", new JavaLanguageHandler(13)); addVersion("14", new JavaLanguageHandler(14)); addVersion("15", new JavaLanguageHandler(15)); - addVersion("15-preview", new JavaLanguageHandler(15, true)); - addDefaultVersion("16", new JavaLanguageHandler(16)); // 16 is the default + addVersion("16", new JavaLanguageHandler(16)); addVersion("16-preview", new JavaLanguageHandler(16, true)); + addDefaultVersion("17", new JavaLanguageHandler(17)); // 17 is the default + addVersion("17-preview", new JavaLanguageHandler(17, true)); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTypeDeclaration.java index 1e7c3685a7..32f4785c6c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTypeDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTypeDeclaration.java @@ -4,8 +4,11 @@ package net.sourceforge.pmd.lang.java.ast; +import net.sourceforge.pmd.lang.ast.Node; + /** * The declaration of an annotation type. + * This is a {@linkplain Node#isFindBoundary() find boundary} for tree traversal methods. * *

Note that in contrast to interface types, no {@linkplain ASTExtendsList extends clause} * is permitted, and an annotation type cannot be generic. diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java index 1969d8e16c..4834af36a1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java @@ -4,19 +4,17 @@ package net.sourceforge.pmd.lang.java.ast; -import static net.sourceforge.pmd.util.CollectionUtil.listOf; - -import java.util.Collections; -import java.util.List; - import org.checkerframework.checker.nullness.qual.NonNull; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.NodeStream; import net.sourceforge.pmd.lang.java.types.JTypeMirror; /** * An anonymous class declaration. This can occur in a {@linkplain ASTConstructorCall class instance creation * expression} * or in an {@linkplain ASTEnumConstant enum constant declaration}. + * This is a {@linkplain Node#isFindBoundary() find boundary} for tree traversal methods. * * *

@@ -44,15 +42,15 @@ public final class ASTAnonymousClassDeclaration extends AbstractAnyTypeDeclarati
     }
 
     @Override
-    public @NonNull List getSuperInterfaceTypeNodes() {
+    public @NonNull NodeStream getSuperInterfaceTypeNodes() {
         if (getParent() instanceof ASTConstructorCall) {
             ASTConstructorCall ctor = (ASTConstructorCall) getParent();
             @NonNull JTypeMirror type = ctor.getTypeMirror();
             if (type.isInterface()) {
-                return listOf(ctor.getTypeNode());
+                return NodeStream.of(ctor.getTypeNode());
             }
         }
-        return Collections.emptyList();
+        return NodeStream.empty();
     }
 
     @Override
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java
index 799249e581..5a51c7b109 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java
@@ -6,8 +6,6 @@ package net.sourceforge.pmd.lang.java.ast;
 
 import static net.sourceforge.pmd.lang.java.ast.JModifier.ABSTRACT;
 
-import java.util.List;
-
 import org.checkerframework.checker.nullness.qual.NonNull;
 import org.checkerframework.checker.nullness.qual.Nullable;
 
@@ -279,9 +277,8 @@ public interface ASTAnyTypeDeclaration
      * Returns the list of interfaces implemented by this class, or
      * extended by this interface. Returns null if no such list is declared.
      */
-    @NonNull
-    default List getSuperInterfaceTypeNodes() {
-        return ASTList.orEmpty(isInterface() ? getFirstChildOfType(ASTExtendsList.class)
-                                             : getFirstChildOfType(ASTImplementsList.class));
+    default @NonNull NodeStream getSuperInterfaceTypeNodes() {
+        return ASTList.orEmptyStream(isInterface() ? firstChild(ASTExtendsList.class)
+                                                   : firstChild(ASTImplementsList.class));
     }
 }
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java
index e4f3502f82..82a552135f 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java
@@ -6,13 +6,12 @@ package net.sourceforge.pmd.lang.java.ast;
 
 import java.util.List;
 
-import net.sourceforge.pmd.annotation.Experimental;
 import net.sourceforge.pmd.lang.ast.Node;
 
 
 /**
- * Represents class and interface declarations. This is a {@linkplain Node#isFindBoundary() find boundary}
- * for tree traversal methods.
+ * Represents class and interface declarations.
+ * This is a {@linkplain Node#isFindBoundary() find boundary} for tree traversal methods.
  *
  * 
  *
@@ -81,9 +80,7 @@ public final class ASTClassOrInterfaceDeclaration extends AbstractAnyTypeDeclara
     }
 
 
-    @Experimental
     public List getPermittedSubclasses() {
         return ASTList.orEmpty(children(ASTPermitsList.class).first());
     }
-
 }
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.java
index 8d06c5f957..f20818f7a0 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.java
@@ -7,8 +7,8 @@ package net.sourceforge.pmd.lang.java.ast;
 import net.sourceforge.pmd.lang.ast.Node;
 
 /**
- * Represents an enum declaration. This is a {@linkplain Node#isFindBoundary() find boundary}
- * for tree traversal methods.
+ * Represents an enum declaration.
+ * This is a {@linkplain Node#isFindBoundary() find boundary} for tree traversal methods.
  *
  * 

An enum declaration is implicitly final unless it contains at * least one enum constant that has a class body. A nested enum type diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTGuardedPattern.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTGuardedPattern.java new file mode 100644 index 0000000000..82995a870f --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTGuardedPattern.java @@ -0,0 +1,52 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import net.sourceforge.pmd.annotation.Experimental; + +/** + * A guarded pattern (JDK17 Preview). This can be found + * in {@link ASTSwitchLabel}s. + * + *

+ *
+ * GuardedPattern ::= {@linkplain ASTPattern Pattern} "&&" {@linkplain ASTConditionalAndExpression ConditionalAndExpression}
+ *
+ * 
+ * + * @see JEP 406: Pattern Matching for switch (Preview) +*/ +@Experimental +public final class ASTGuardedPattern extends AbstractJavaNode implements ASTPattern { + + private int parenDepth; + + ASTGuardedPattern(int id) { + super(id); + } + + @Override + protected R acceptVisitor(JavaVisitor visitor, P data) { + return visitor.visit(this, data); + } + + public ASTPattern getPattern() { + return (ASTPattern) getChild(0); + } + + public JavaNode getGuard() { + return getChild(1); + } + + void bumpParenDepth() { + parenDepth++; + } + + @Override + @Experimental + public int getParenthesisDepth() { + return parenDepth; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPattern.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPattern.java index 5a2b275d2c..122f5294dc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPattern.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPattern.java @@ -4,9 +4,11 @@ package net.sourceforge.pmd.lang.java.ast; +import net.sourceforge.pmd.annotation.Experimental; + /** - * A pattern (for pattern matching constructs like {@link ASTInstanceOfExpression InstanceOfExpression}). - * This is a JDK 16 feature. + * A pattern (for pattern matching constructs like {@link ASTInstanceOfExpression InstanceOfExpression} + * or within a {@link ASTSwitchLabel}). This is a JDK 16 feature. * *

This interface will be implemented by all forms of patterns. For * now, only type test patterns are supported. Record deconstruction @@ -14,7 +16,8 @@ package net.sourceforge.pmd.lang.java.ast; * *

  *
- * Pattern ::= {@link ASTTypePattern TypePattern}
+ * Pattern ::=   {@link ASTTypePattern TypePattern}
+ *             | {@link ASTGuardedPattern GuardedPattern}
  *
  * 
* @@ -22,4 +25,10 @@ package net.sourceforge.pmd.lang.java.ast; */ public interface ASTPattern extends JavaNode { + /** + * Returns the number of parenthesis levels around this pattern. + * If this method returns 0, then no parentheses are present. + */ + @Experimental + int getParenthesisDepth(); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPermitsList.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPermitsList.java index c9fc9e9f25..29c0d1d394 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPermitsList.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPermitsList.java @@ -4,16 +4,15 @@ package net.sourceforge.pmd.lang.java.ast; -import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.java.ast.ASTList.ASTNonEmptyList; /** * Represents the {@code permits} clause of a (sealed) class declaration. * - *

This is a Java 15 Preview and Java 16 Preview feature. + *

This is a Java 17 Feature. * - *

See https://openjdk.java.net/jeps/397 + *

See https://openjdk.java.net/jeps/409 * *

  *
@@ -21,7 +20,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTList.ASTNonEmptyList;
  *                ( "," ClassOrInterfaceType )*
  * 
*/ -@Experimental public final class ASTPermitsList extends ASTNonEmptyList { ASTPermitsList(int id) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordDeclaration.java index 6953460ae6..d264b51603 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTRecordDeclaration.java @@ -43,11 +43,6 @@ public final class ASTRecordDeclaration extends AbstractAnyTypeDeclaration { return getFirstChildOfType(ASTRecordBody.class).children(ASTBodyDeclaration.class); } - @Override - public boolean isFindBoundary() { - return isNested(); - } - @Override @NonNull public ASTRecordComponentList getRecordComponents() { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypePattern.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypePattern.java index 44f92d9f61..3a5f0b9a84 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypePattern.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypePattern.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.java.ast; +import net.sourceforge.pmd.annotation.Experimental; + /** * A type pattern (JDK16). This can be found on * the right-hand side of an {@link ASTInfixExpression InstanceOfExpression}, @@ -19,6 +21,8 @@ package net.sourceforge.pmd.lang.java.ast; */ public final class ASTTypePattern extends AbstractJavaNode implements ASTPattern, AccessNode { + private int parenDepth; + ASTTypePattern(int id) { super(id); } @@ -39,4 +43,14 @@ public final class ASTTypePattern extends AbstractJavaNode implements ASTPattern public ASTVariableDeclaratorId getVarId() { return getFirstChildOfType(ASTVariableDeclaratorId.class); } + + void bumpParenDepth() { + parenDepth++; + } + + @Override + @Experimental + public int getParenthesisDepth() { + return parenDepth; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java index 7bba45bee1..f492d9b9f2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.types.JClassType; import net.sourceforge.pmd.lang.rule.xpath.DeprecatedAttribute; @@ -15,6 +16,7 @@ import net.sourceforge.pmd.util.document.FileLocation; /** * Abstract class for type declarations nodes. + * This is a {@linkplain Node#isFindBoundary() find boundary} for tree traversal methods. */ abstract class AbstractAnyTypeDeclaration extends AbstractTypedSymbolDeclarator implements ASTAnyTypeDeclaration, LeftRecursiveNode { @@ -79,5 +81,10 @@ abstract class AbstractAnyTypeDeclaration extends AbstractTypedSymbolDeclarator< public @NonNull JClassType getTypeMirror() { return (JClassType) super.getTypeMirror(); } + + @Override + public boolean isFindBoundary() { + return isNested(); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstImplUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstImplUtil.java index 0ee7dd9738..b813434b7c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstImplUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AstImplUtil.java @@ -48,4 +48,14 @@ final class AstImplUtil { ((AbstractJavaExpr) expression).bumpParenDepth(); } + static void bumpParenDepth(ASTPattern pattern) { + assert pattern instanceof ASTTypePattern || pattern instanceof ASTGuardedPattern + : pattern.getClass() + " doesn't have parenDepth attribute!"; + + if (pattern instanceof ASTTypePattern) { + ((ASTTypePattern) pattern).bumpParenDepth(); + } else if (pattern instanceof ASTGuardedPattern) { + ((ASTGuardedPattern) pattern).bumpParenDepth(); + } + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java index b039057321..4e2f927db4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java @@ -22,13 +22,16 @@ import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTForeachStatement; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; +import net.sourceforge.pmd.lang.java.ast.ASTGuardedPattern; import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTIntersectionType; import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; import net.sourceforge.pmd.lang.java.ast.ASTModuleDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTPattern; import net.sourceforge.pmd.lang.java.ast.ASTReceiverParameter; import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTResource; @@ -137,8 +140,28 @@ public class LanguageLevelChecker { /** * @see JEP 360: Sealed Classes (Preview) * @see JEP 397: Sealed Classes (Second Preview) + * @see JEP 409: Sealed Classes */ - SEALED_CLASSES(15, 16, false), + SEALED_CLASSES(15, 16, true), + + /** + * @see JEP 406: Pattern Matching for switch (Preview) + */ + PATTERN_MATCHING_FOR_SWITCH(17, 17, false), + + /** + * Part of pattern matching for switch + * @see #PATTERN_MATCHING_FOR_SWITCH + * @see JEP 406: Pattern Matching for switch (Preview) + */ + GUARDED_PATTERNS(17, 17, false), + + /** + * Part of pattern matching for switch + * @see #PATTERN_MATCHING_FOR_SWITCH + * @see JEP 406: Pattern Matching for switch (Preview) + */ + NULL_CASE_LABELS(17, 17, false), ; // SUPPRESS CHECKSTYLE enum trailing semi is awesome @@ -491,6 +514,12 @@ public class LanguageLevelChecker { return null; } + @Override + public Void visit(ASTGuardedPattern node, T data) { + check(node, PreviewFeature.GUARDED_PATTERNS, data); + return null; + } + @Override public Void visit(ASTTryStatement node, T data) { if (node.isTryWithResources()) { @@ -529,6 +558,18 @@ public class LanguageLevelChecker { if (IteratorUtil.count(node.iterator()) > 1) { check(node, RegularLanguageFeature.COMPOSITE_CASE_LABEL, data); } + if (node.isDefault() && "case".equals(node.getFirstToken().getImage())) { + check(node, PreviewFeature.PATTERN_MATCHING_FOR_SWITCH, data); + } + if (node.getFirstChild() instanceof ASTGuardedPattern) { + check(node, PreviewFeature.GUARDED_PATTERNS, data); + } + if (node.getFirstChild() instanceof ASTPattern) { + check(node, PreviewFeature.PATTERN_MATCHING_FOR_SWITCH, data); + } + if (node.getFirstChild() instanceof ASTNullLiteral) { + check(node, PreviewFeature.NULL_CASE_LABELS, data); + } return null; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java index 8580aa9e47..73efb9c94e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -52,7 +52,6 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse return false; } - @Override public boolean dependsOn(AstProcessingStage stage) { if (!(stage instanceof JavaProcessingStage)) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index 5bef6cf500..6f3599bb90 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -27,6 +27,6 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule .none(TestFrameworksUtil::isProbableAssertCall)) { addViolation(data, method); } - return data; + return super.visit(method, data); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java index a7f3dbef26..d58cd084c9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java @@ -155,6 +155,3 @@ public class MissingOverrideRule extends AbstractJavaRule { } } - - - diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PrimitiveWrapperInstantiationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PrimitiveWrapperInstantiationRule.java new file mode 100644 index 0000000000..a43f94f73d --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PrimitiveWrapperInstantiationRule.java @@ -0,0 +1,110 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices; + +import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; +import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.InvocationMatcher; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; + +public class PrimitiveWrapperInstantiationRule extends AbstractJavaRulechainRule { + + private static final InvocationMatcher BOOLEAN_VALUEOF_MATCHER = InvocationMatcher.parse("java.lang.Boolean#valueOf(_)"); + + public PrimitiveWrapperInstantiationRule() { + super(ASTConstructorCall.class, ASTMethodCall.class); + } + + @Override + public Object visit(ASTConstructorCall node, Object data) { + ASTClassOrInterfaceType type = node.firstChild(ASTClassOrInterfaceType.class); + if (type == null) { + return data; + } + + if (TypeTestUtil.isA(Double.class, type) + || TypeTestUtil.isA(Float.class, type) + || TypeTestUtil.isA(Long.class, type) + || TypeTestUtil.isA(Integer.class, type) + || TypeTestUtil.isA(Short.class, type) + || TypeTestUtil.isA(Byte.class, type) + || TypeTestUtil.isA(Character.class, type)) { + addViolation(data, node, type.getSimpleName()); + } else if (TypeTestUtil.isA(Boolean.class, type)) { + checkArguments(node.getArguments(), node, data); + } + + return data; + } + + /** + * Finds calls of "Boolean.valueOf". + */ + @Override + public Object visit(ASTMethodCall node, Object data) { + if (BOOLEAN_VALUEOF_MATCHER.matchesCall(node)) { + checkArguments(node.getArguments(), node, data); + } + + return data; + } + + private void checkArguments(ASTArgumentList arguments, JavaNode node, Object data) { + if (arguments == null || arguments.size() != 1) { + return; + } + boolean isNewBoolean = node instanceof ASTConstructorCall; + String messagePart = isNewBoolean + ? "Do not use `new Boolean" + : "Do not use `Boolean.valueOf"; + ASTStringLiteral stringLiteral = getFirstArgStringLiteralOrNull(arguments); + ASTBooleanLiteral boolLiteral = getFirstArgBooleanLiteralOrNull(arguments); + if (stringLiteral != null) { + if ("\"true\"".equals(stringLiteral.getImage())) { + addViolationWithMessage(data, node, messagePart + "(\"true\")`, prefer `Boolean.TRUE`"); + } else if ("\"false\"".equals(stringLiteral.getImage())) { + addViolationWithMessage(data, node, messagePart + "(\"false\")`, prefer `Boolean.FALSE`"); + } else { + addViolationWithMessage(data, node, messagePart + "(\"...\")`, prefer `Boolean.valueOf`"); + } + } else if (boolLiteral != null) { + if (boolLiteral.isTrue()) { + addViolationWithMessage(data, node, messagePart + "(true)`, prefer `Boolean.TRUE`"); + } else { + addViolationWithMessage(data, node, messagePart + "(false)`, prefer `Boolean.FALSE`"); + } + } else if (isNewBoolean) { + // any argument with "new Boolean", might be a variable access + addViolationWithMessage(data, node, messagePart + "(...)`, prefer `Boolean.valueOf`"); + } + } + + private static ASTStringLiteral getFirstArgStringLiteralOrNull(ASTArgumentList arguments) { + if (arguments.size() == 1) { + ASTExpression firstArg = arguments.get(0); + if (firstArg instanceof ASTStringLiteral) { + return (ASTStringLiteral) firstArg; + } + } + return null; + } + + private static ASTBooleanLiteral getFirstArgBooleanLiteralOrNull(ASTArgumentList arguments) { + if (arguments.size() == 1) { + ASTExpression firstArg = arguments.get(0); + if (firstArg instanceof ASTBooleanLiteral) { + return (ASTBooleanLiteral) firstArg; + } + } + return null; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SimplifiableTestAssertionRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SimplifiableTestAssertionRule.java new file mode 100644 index 0000000000..a34795872d --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SimplifiableTestAssertionRule.java @@ -0,0 +1,327 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices; + +import java.util.HashSet; +import java.util.Set; + +import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; +import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; +import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; +import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpressionNotPlusMinus; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.ast.TypeNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; + +/** + * + */ +public class SimplifiableTestAssertionRule extends AbstractJavaRule { + + private final Set importedMethodsHere = new HashSet<>(); + private boolean allAssertionsOn; + + @Override + public Object visit(ASTCompilationUnit node, Object data) { + importedMethodsHere.clear(); + allAssertionsOn = false; + for (ASTImportDeclaration importDecl : node.findChildrenOfType(ASTImportDeclaration.class)) { + if (importDecl.isStatic()) { + if (importDecl.isImportOnDemand()) { + if (isAssertionContainer(importDecl.getImportedName())) { + // import static org.junit.Assert.* + allAssertionsOn = true; + } + } else { + checkImportedAssertion(importDecl.getImportedName()); + } + } + } + + super.visit(node, data); + return null; + } + + @Override + public Object visit(ASTPrimaryExpression node, Object data) { + final boolean isAssertTrue = isAssertionCall(node, "assertTrue"); + final boolean isAssertFalse = isAssertionCall(node, "assertFalse"); + + if (isAssertTrue || isAssertFalse) { + ASTArgumentList args = getNonEmptyArgList(node); + JavaNode lastArg = getChildRev(args, -1); + ASTEqualityExpression eq = asEqualityExpr(lastArg); + if (eq != null) { + boolean isPositive = isPositiveEqualityExpr(eq) == isAssertTrue; + final String suggestion; + if (isNullLiteral(eq.getChild(0)) + || isNullLiteral(eq.getChild(1))) { + // use assertNull/assertNonNull + suggestion = isPositive ? "assertNull" : "assertNonNull"; + } else { + if (isPrimitive(eq.getChild(0)) || isPrimitive(eq.getChild(1))) { + suggestion = isPositive ? "assertEquals" : "assertNotEquals"; + } else { + suggestion = isPositive ? "assertSame" : "assertNotSame"; + } + } + addViolation(data, node, suggestion); + + } else { + JavaNode negatedExprOperand = getNegatedExprOperand(lastArg); // nullable + + if (isCall(negatedExprOperand, "equals")) { + //assertTrue(!a.equals(b)) + String suggestion = isAssertTrue ? "assertNotEquals" : "assertEquals"; + addViolation(data, node, suggestion); + + } else if (negatedExprOperand != null) { + //assertTrue(!something) + String suggestion = isAssertTrue ? "assertFalse" : "assertTrue"; + addViolation(data, node, suggestion); + + } else if (isCall(lastArg, "equals")) { + //assertTrue(a.equals(b)) + String suggestion = isAssertTrue ? "assertEquals" : "assertNotEquals"; + addViolation(data, node, suggestion); + } + } + } + + boolean isAssertEquals = isAssertionCall(node, "assertEquals"); + boolean isAssertNotEquals = isAssertionCall(node, "assertNotEquals"); + + if (isAssertEquals || isAssertNotEquals) { + ASTArgumentList argList = getNonEmptyArgList(node); + if (argList != null && argList.size() >= 2) { + JavaNode comp0 = getChildRev(argList, -1); + JavaNode comp1 = getChildRev(argList, -2); + if (isBooleanLiteral(comp0) ^ isBooleanLiteral(comp1)) { + if (isBooleanLiteral(comp1)) { + JavaNode tmp = comp0; + comp0 = comp1; + comp1 = tmp; + } + // now the literal is in comp0 and the other is some expr + if (comp1 instanceof TypeNode && TypeTestUtil.isA(boolean.class, (TypeNode) comp1)) { + ASTBooleanLiteral literal = (ASTBooleanLiteral) unwrapLiteral(comp0); + String suggestion = literal.isTrue() == isAssertEquals ? "assertTrue" : "assertFalse"; + addViolation(data, node, suggestion); + } + } + } + } + + return super.visit(node, data); + } + + private boolean isPrimitive(JavaNode node) { + if (node instanceof TypeNode) { + Class t0 = ((TypeNode) node).getType(); + return t0 != null && t0.isPrimitive(); + } + return false; + } + + /** + * Returns a child with an offset from the end. Eg {@code getChildRev(list, -1)} + * returns the last child. + */ + private static JavaNode getChildRev(JavaNode list, int i) { + assert i < 0 : "Expecting negative offset"; + return list == null ? null : list.getChild(list.getNumChildren() + i); + } + + /** + * Checks if the node is a call to a method which has the given name. + * The receiver expression may be arbitrarily complicated. + */ + private static boolean isCall(JavaNode node, String methodName) { + if (node instanceof ASTExpression) { + if (node.getNumChildren() == 1) { + node = node.getChild(0); + } else { + return false; + } + } + if (!(node instanceof ASTPrimaryExpression) || node.getNumChildren() < 2) { + return false; + } + + + JavaNode prefix = getChildRev(node, -2); + JavaNode suffix = getChildRev(node, -1); + if (!(suffix instanceof ASTPrimarySuffix) || !((ASTPrimarySuffix) suffix).isArguments()) { + return false; + } + // we know it's a method call + if (prefix instanceof ASTPrimaryPrefix + && prefix.getNumChildren() > 0 + && prefix.getChild(0) instanceof ASTName) { + String image = prefix.getChild(0).getImage(); + return isPossiblyQualifiedMethodName(methodName, image); + } else if (prefix instanceof ASTPrimarySuffix) { + // call chain + return methodName.equals(prefix.getImage()); + } + + return false; + } + + private boolean isAssertionCall(JavaNode node, String methodName) { + if (node instanceof ASTExpression) { + if (node.getNumChildren() == 1) { + node = node.getChild(0); + } else { + return false; + } + } + if (node.getNumChildren() != 2 || !isCall(node, methodName)) { + return false; // not a call chain + } + + ASTPrimaryPrefix prefix = (ASTPrimaryPrefix) getChildRev(node, -2); + return isAssertionMethodName(methodName, (ASTName) prefix.getChild(0)); + } + + private static boolean isPossiblyQualifiedMethodName(String methodName, String possiblyQualifiedName) { + return methodName.equals(possiblyQualifiedName) + || possiblyQualifiedName.length() > methodName.length() + && possiblyQualifiedName.endsWith(methodName) + && possiblyQualifiedName.charAt(possiblyQualifiedName.length() - methodName.length() - 1) == '.'; + } + + private boolean isAssertionMethodName(String methodName, ASTName location) { + String possiblyQualifiedName = location.getImage(); + if (methodName.equals(possiblyQualifiedName)) { + return allAssertionsOn + || importedMethodsHere.contains(methodName) + || TypeTestUtil.isA("junit.framework.TestCase", location.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class)); + } + if (possiblyQualifiedName.length() > methodName.length() + && possiblyQualifiedName.endsWith(methodName) + && possiblyQualifiedName.charAt(possiblyQualifiedName.length() - methodName.length() - 1) == '.') { + return TypeTestUtil.isA("org.junit.jupiter.api.Assertions", location) + || TypeTestUtil.isA("org.junit.Assert", location); + } + return false; + } + + + private /*nullable*/ ASTArgumentList getNonEmptyArgList(ASTPrimaryExpression node) { + ASTPrimarySuffix suffix = node.getFirstChildOfType(ASTPrimarySuffix.class); + if (suffix != null && suffix.isArguments() && suffix.getArgumentCount() > 0) { + return (ASTArgumentList) suffix.getChild(0).getChild(0); + } + return null; + } + + private ASTEqualityExpression asEqualityExpr(JavaNode node) { + if (node instanceof ASTExpression) { + if (node.getNumChildren() == 1) { + node = node.getChild(0); + } else { + return null; + } + } + return node instanceof ASTEqualityExpression ? (ASTEqualityExpression) node + : null; + } + + private boolean isPositiveEqualityExpr(ASTEqualityExpression node) { + return node != null && node.getImage().equals("=="); + } + + private static JavaNode getNegatedExprOperand(JavaNode node) { + // /Expression/UnaryExpressionNotPlusMinus[@Image='!'] + // /PrimaryExpression/PrimaryPrefix + if (node instanceof ASTExpression) { + if (node.getNumChildren() == 1) { + node = node.getChild(0); + } else { + return null; + } + } + if (node instanceof ASTUnaryExpressionNotPlusMinus + && "!".equals(((ASTUnaryExpressionNotPlusMinus) node).getOperator())) { + return node.getChild(0); + } + return null; + } + + private static boolean isNullLiteral(JavaNode node) { + return unwrapLiteral(node) instanceof ASTNullLiteral; + } + + private static boolean isBooleanLiteral(JavaNode node) { + return unwrapLiteral(node) instanceof ASTBooleanLiteral; + } + + private static JavaNode unwrapLiteral(JavaNode node) { + if (node instanceof ASTExpression) { + if (node.getNumChildren() == 1) { + node = node.getChild(0); + } else { + return null; + } + } + if (node instanceof ASTPrimaryExpression) { + if (node.getNumChildren() == 1) { + node = node.getChild(0); + } else { + return null; + } + } + if (node instanceof ASTPrimaryPrefix) { + if (node.getNumChildren() == 1) { + node = node.getChild(0); + } else { + return null; + } + } + if (node instanceof ASTLiteral) { + if (node.getNumChildren() == 1) { + node = node.getChild(0); + } else { + return null; + } + } + return node; + } + + + private boolean isAssertionContainer(String importedName) { + return "org.junit.jupiter.api.Assertions".equals(importedName) + || "org.junit.Assert".equals(importedName); + } + + private void checkImportedAssertion(String importedName) { + String stripped = removePrefixOrNull(importedName, "org.junit.jupiter.api.Assertions."); + if (stripped == null) { + stripped = removePrefixOrNull(importedName, "org.junit.Assert."); + } + if (stripped != null && stripped.indexOf('.') == -1) { + importedMethodsHere.add(stripped); + } + } + + private static String removePrefixOrNull(String str, String prefix) { + if (str.startsWith(prefix)) { + return str.substring(prefix.length()); + } + return null; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java index a0d1b3e114..607d35a77e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java @@ -30,7 +30,7 @@ public class UnusedFormalParameterRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTConstructorDeclaration node, Object data) { check(node, data); - return data; + return super.visit(node, data); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseTryWithResourcesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseTryWithResourcesRule.java new file mode 100644 index 0000000000..7d3f7ed962 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseTryWithResourcesRule.java @@ -0,0 +1,63 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices; + +import static net.sourceforge.pmd.properties.PropertyFactory.stringListProperty; + +import java.util.List; + +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFinallyClause; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTTryStatement; +import net.sourceforge.pmd.lang.java.ast.ASTTypeExpression; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.properties.PropertyDescriptor; + +public final class UseTryWithResourcesRule extends AbstractJavaRulechainRule { + + private static final PropertyDescriptor> CLOSE_METHODS = + stringListProperty("closeMethods") + .desc("Method names in finally block, which trigger this rule") + .defaultValues("close", "closeQuietly") + .delim(',') + .build(); + + public UseTryWithResourcesRule() { + super(ASTTryStatement.class); + definePropertyDescriptor(CLOSE_METHODS); + } + + @Override + public Object visit(ASTTryStatement node, Object data) { + boolean isJava9OrLater = node.getLanguageVersion().compareToVersion("9") >= 0; + + ASTFinallyClause finallyClause = node.getFinallyClause(); + if (finallyClause != null) { + List methods = finallyClause.descendants(ASTMethodCall.class) + .filter(m -> getProperty(CLOSE_METHODS).contains(m.getMethodName())) + .toList(); + for (ASTMethodCall method : methods) { + ASTExpression closeTarget = method.getQualifier(); + if (!(closeTarget instanceof ASTTypeExpression) // ignore static method calls + && TypeTestUtil.isA(AutoCloseable.class, closeTarget) + && (isJava9OrLater || JavaRuleUtil.isReferenceToLocal(closeTarget)) + || hasAutoClosableArguments(method)) { + addViolation(data, node); + break; // only report the first closeable + } + } + } + return data; + } + + private boolean hasAutoClosableArguments(ASTMethodCall method) { + return method.getArguments().children() + .filter(e -> TypeTestUtil.isA(AutoCloseable.class, e)) + .nonEmpty(); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java index 1eca7b7e1a..1d94d8f868 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java @@ -26,7 +26,7 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule interfaceRegex = defaultProp("interface").build(); private final PropertyDescriptor enumerationRegex = defaultProp("enum").build(); private final PropertyDescriptor annotationRegex = defaultProp("annotation").build(); - private final PropertyDescriptor utilityClassRegex = defaultProp("utility class").defaultValue("[A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants)").build(); + private final PropertyDescriptor utilityClassRegex = defaultProp("utility class").build(); public ClassNamingConventionsRule() { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryBoxingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryBoxingRule.java new file mode 100644 index 0000000000..33f56b41b8 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryBoxingRule.java @@ -0,0 +1,202 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.codestyle; + +import static net.sourceforge.pmd.util.CollectionUtil.setOf; + +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTList; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.InvocationNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.JMethodSig; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; +import net.sourceforge.pmd.lang.java.types.OverloadSelectionResult; +import net.sourceforge.pmd.lang.java.types.TypePrettyPrint; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.java.types.ast.ExprContext; + +/** + * + */ +public class UnnecessaryBoxingRule extends AbstractJavaRulechainRule { + + private static final Set INTERESTING_NAMES = setOf( + "valueOf", + "booleanValue", + "charValue", + "byteValue", + "shortValue", + "intValue", + "longValue", + "floatValue", + "doubleValue" + ); + + public UnnecessaryBoxingRule() { + super(ASTMethodCall.class, ASTConstructorCall.class); + } + + @Override + public Object visit(ASTConstructorCall node, Object data) { + if (node.getTypeMirror().isBoxedPrimitive()) { + ASTExpression arg = ASTList.singleOrNull(node.getArguments()); + if (arg == null) { + return null; + } + JTypeMirror argT = arg.getTypeMirror(); + if (argT.isPrimitive()) { + checkBox((RuleContext) data, "boxing", node, arg, node.getMethodType().getFormalParameters().get(0)); + } + } + return null; + } + + + @Override + public Object visit(ASTMethodCall node, Object data) { + if (INTERESTING_NAMES.contains(node.getMethodName())) { + OverloadSelectionResult overload = node.getOverloadSelectionInfo(); + if (overload.isFailed()) { + return null; + } + JMethodSig m = overload.getMethodType(); + boolean isValueOf = "valueOf".equals(node.getMethodName()); + ASTExpression qualifier = node.getQualifier(); + + if (isValueOf && isWrapperValueOf(m)) { + checkBox((RuleContext) data, "boxing", node, node.getArguments().get(0), m.getFormalParameters().get(0)); + } else if (isValueOf && isStringValueOf(m) && qualifier != null) { + checkUnboxing((RuleContext) data, node, qualifier.getTypeMirror()); + } else if (!isValueOf && isUnboxingCall(m) && qualifier != null) { + checkBox((RuleContext) data, "unboxing", node, qualifier, qualifier.getTypeMirror()); + } + } + return null; + } + + private boolean isUnboxingCall(JMethodSig m) { + return !m.isStatic() && m.getDeclaringType().isBoxedPrimitive() && m.getArity() == 0; + } + + private boolean isWrapperValueOf(JMethodSig m) { + return m.isStatic() + && m.getArity() == 1 + && m.getDeclaringType().isBoxedPrimitive() + && m.getFormalParameters().get(0).isPrimitive(); + } + + private boolean isStringValueOf(JMethodSig m) { + return m.isStatic() + && (m.getArity() == 1 || m.getArity() == 2) + && m.getDeclaringType().isBoxedPrimitive() + && TypeTestUtil.isA(String.class, m.getFormalParameters().get(0)); + } + + private void checkBox( + RuleContext rctx, + String opKind, + ASTExpression conversionExpr, + ASTExpression convertedExpr, + JTypeMirror conversionInput + ) { + // the conversion looks like + // CTX _ = conversion(sourceExpr) + + // we have the following data flow: + // sourceExpr -> convInput -> convOutput -> ctx + // 1 2 3 + // where 1 and 3 are implicit conversions which we assume are + // valid because the code should compile. + + // we want to report a violation if this is equivalent to + // sourceExpr -> ctx + + // which basically means testing that convInput -> convOutput + // may be performed implicitly. + + // We cannot just test compatibility of the source to the ctx, + // because of situations like + // int i = integer.byteValue() + // where the conversion actually truncates the input value. + + JTypeMirror sourceType = convertedExpr.getTypeMirror(); + JTypeMirror conversionOutput = conversionExpr.getTypeMirror(); + ExprContext ctx = conversionExpr.getConversionContext(); + JTypeMirror ctxType = ctx.getTargetType(); + if (ctxType == null && conversionExpr instanceof InvocationNode) { + ctxType = conversionOutput; + } + + if (ctxType != null) { + + if (isImplicitlyConvertible(conversionInput, conversionOutput)) { + + boolean simpleConv = isReferenceSubtype(sourceType, conversionInput); + + final String reason; + if (simpleConv && conversionInput.unbox().equals(conversionOutput)) { + reason = "explicit unboxing"; + } else if (simpleConv && conversionInput.box().equals(conversionOutput)) { + reason = "explicit boxing"; + } else if (sourceType.equals(conversionOutput)) { + reason = "boxing of boxed value"; + } else { + if (sourceType.equals(ctxType)) { + reason = opKind; + } else { + reason = "explicit conversion from " + TypePrettyPrint.prettyPrintWithSimpleNames(sourceType) + " to " + TypePrettyPrint.prettyPrintWithSimpleNames(ctxType); + } + } + + addViolation(rctx, conversionExpr, reason); + } + } + } + + private void checkUnboxing( + RuleContext rctx, + ASTMethodCall methodCall, + JTypeMirror conversionOutput + ) { + // methodCall is e.g. Integer.valueOf("42") + // this checks, whether the resulting type "Integer" is e.g. assigned to an "int" + // which triggers implicit unboxing. + ExprContext ctx = methodCall.getConversionContext(); + JTypeMirror ctxType = ctx.getTargetType(); + + if (ctxType != null) { + if (isImplicitlyConvertible(conversionOutput, ctxType)) { + if (conversionOutput.unbox().equals(ctxType)) { + addViolation(rctx, methodCall, "implicit unboxing. Use " + + conversionOutput.getSymbol().getSimpleName() + ".parse" + + StringUtils.capitalize(ctxType.getSymbol().getSimpleName()) + "(...) instead"); + } + } + } + } + + private boolean isImplicitlyConvertible(JTypeMirror i, JTypeMirror o) { + return i.box().isSubtypeOf(o.box()) + || i.unbox().isSubtypeOf(o.unbox()); + } + + /** + * Whether {@code S <: T}, but ignoring primitive widening. + * {@code isReferenceSubtype(int, double) == false} even though + * {@code int.isSubtypeOf(double)}. + */ + private static boolean isReferenceSubtype(JTypeMirror s, JTypeMirror t) { + return s.isPrimitive() ? t.equals(s) + : s.isSubtypeOf(t); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryReturnRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryReturnRule.java index dc4586ae08..74110e7693 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryReturnRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryReturnRule.java @@ -82,5 +82,4 @@ public class UnnecessaryReturnRule extends AbstractJavaRulechainRule { private static boolean isBranchOfSwitchExpr(ASTSwitchBranch branch) { return branch.getParent() instanceof ASTSwitchExpression; } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ClassWithOnlyPrivateConstructorsShouldBeFinalRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ClassWithOnlyPrivateConstructorsShouldBeFinalRule.java new file mode 100644 index 0000000000..85720dedc4 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ClassWithOnlyPrivateConstructorsShouldBeFinalRule.java @@ -0,0 +1,49 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.design; + +import static net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility.V_PRIVATE; + +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; + +public class ClassWithOnlyPrivateConstructorsShouldBeFinalRule extends AbstractJavaRulechainRule { + + public ClassWithOnlyPrivateConstructorsShouldBeFinalRule() { + super(ASTClassOrInterfaceDeclaration.class); + } + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (node.isRegularClass() + && !node.isFinal() + && hasOnlyPrivateCtors(node) + && hasNoSubclasses(node)) { + addViolation(data, node); + } + return null; + } + + private boolean hasNoSubclasses(ASTClassOrInterfaceDeclaration klass) { + return klass.getRoot() + .descendants(ASTAnyTypeDeclaration.class) + .crossFindBoundaries() + .none(it -> doesExtend(it, klass)); + } + + private boolean doesExtend(ASTAnyTypeDeclaration sub, ASTClassOrInterfaceDeclaration superClass) { + return sub != superClass && TypeTestUtil.isA(superClass.getTypeMirror(), sub); + } + + private boolean hasOnlyPrivateCtors(ASTClassOrInterfaceDeclaration node) { + return node.getDeclarations(ASTConstructorDeclaration.class).all(it -> it.getVisibility() == V_PRIVATE) + && (node.getVisibility() == V_PRIVATE // then the default ctor is private + || node.getDeclarations(ASTConstructorDeclaration.class).nonEmpty()); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CouplingBetweenObjectsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CouplingBetweenObjectsRule.java index 6c4b95728d..adfd371f42 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CouplingBetweenObjectsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CouplingBetweenObjectsRule.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.java.rule.design; import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; import java.util.HashSet; +import java.util.List; import java.util.Set; import net.sourceforge.pmd.lang.ast.Node; @@ -54,22 +55,14 @@ public class CouplingBetweenObjectsRule extends AbstractJavaRule { typesFoundSoFar = new HashSet<>(); couplingCount = 0; - cu.children().forEach(it -> it.acceptVisitor(this, data)); + Object returnObj = super.visit(cu, data); if (couplingCount > getProperty(THRESHOLD_DESCRIPTOR)) { addViolation(data, cu, "A value of " + couplingCount + " may denote a high amount of coupling within the class"); } - return data; - } - - @Override - public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (node.isInterface()) { - return data; - } - return super.visit(node, data); + return returnObj; } @Override @@ -138,10 +131,18 @@ public class CouplingBetweenObjectsRule extends AbstractJavaRule { * The variable type. */ private void checkVariableType(Node nameNode, String variableType) { + List parentTypes = nameNode.getParentsOfType(ASTClassOrInterfaceDeclaration.class); + // TODO - move this into the symbol table somehow? - if (nameNode.getParentsOfType(ASTClassOrInterfaceDeclaration.class).isEmpty()) { + if (parentTypes.isEmpty()) { return; } + + // skip interfaces + if (parentTypes.get(0).isInterface()) { + return; + } + // if the field is of any type other than the class type // increment the count ClassScope clzScope = ((JavaNode) nameNode).getScope().getEnclosingScope(ClassScope.class); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ExceptionAsFlowControlRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ExceptionAsFlowControlRule.java index 934a62ec1f..0820d8f475 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ExceptionAsFlowControlRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ExceptionAsFlowControlRule.java @@ -9,7 +9,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTCatchClause; import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; import net.sourceforge.pmd.lang.java.ast.ASTTryStatement; import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.types.JTypeMirror; /** @@ -17,11 +17,14 @@ import net.sourceforge.pmd.lang.java.types.JTypeMirror; * * @author Will Sargent */ -public class ExceptionAsFlowControlRule extends AbstractJavaRule { +public class ExceptionAsFlowControlRule extends AbstractJavaRulechainRule { // TODO tests: // - catch a supertype of the exception (unless this is unwanted) // - throw statements with not just a new SomethingExpression, eg a method call returning an exception + public ExceptionAsFlowControlRule() { + super(ASTThrowStatement.class); + } @Override public Object visit(ASTThrowStatement node, Object data) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyConditionalRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyConditionalRule.java new file mode 100644 index 0000000000..a5312867cf --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyConditionalRule.java @@ -0,0 +1,63 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.design; + +import static net.sourceforge.pmd.lang.java.ast.BinaryOp.CONDITIONAL_AND; +import static net.sourceforge.pmd.lang.java.ast.BinaryOp.CONDITIONAL_OR; +import static net.sourceforge.pmd.lang.java.ast.BinaryOp.INSTANCEOF; +import static net.sourceforge.pmd.lang.java.ast.BinaryOp.NE; +import static net.sourceforge.pmd.lang.java.ast.BinaryOp.isInfixExprWithOperator; +import static net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil.getOtherOperandIfInInfixExpr; +import static net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil.isBooleanNegation; +import static net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil.isNullCheck; + +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.StablePathMatcher; + + +public class SimplifyConditionalRule extends AbstractJavaRulechainRule { + + public SimplifyConditionalRule() { + super(ASTInfixExpression.class); + } + + + @Override + public Object visit(ASTInfixExpression node, Object data) { + if (node.getOperator() == INSTANCEOF) { + + StablePathMatcher instanceOfSubject = StablePathMatcher.matching(node.getLeftOperand()); + if (instanceOfSubject == null) { + return null; + } + + ASTExpression nullCheckExpr; + boolean negated; + if (isInfixExprWithOperator(node.getParent(), CONDITIONAL_AND)) { + // a != null && a instanceof T + negated = false; + nullCheckExpr = getOtherOperandIfInInfixExpr(node); + } else if (isBooleanNegation(node.getParent()) + && isInfixExprWithOperator(node.getParent().getParent(), CONDITIONAL_OR)) { + // a == null || a instanceof T + negated = true; + nullCheckExpr = getOtherOperandIfInInfixExpr(node.getParent()); + } else { + return null; + } + + if (!isNullCheck(nullCheckExpr, instanceOfSubject)) { + return null; + } + + if (negated != isInfixExprWithOperator(nullCheckExpr, NE)) { + addViolation(data, nullCheckExpr); + } + } + return null; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityRule.java index 97c3db3541..21f8d2bcb1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityRule.java @@ -6,11 +6,11 @@ package net.sourceforge.pmd.lang.java.rule.design; import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTStatement; -import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabel; +import net.sourceforge.pmd.lang.java.ast.ASTSwitchBranch; +import net.sourceforge.pmd.lang.java.ast.ASTSwitchExpression; +import net.sourceforge.pmd.lang.java.ast.ASTSwitchLike; import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement; -import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -24,6 +24,7 @@ import net.sourceforge.pmd.properties.PropertyFactory; * looking at Subclasses or State Pattern to alleviate the problem.

* * @author David Dixon-Peugh + * @author Clément Fournier */ public class SwitchDensityRule extends AbstractJavaRulechainRule { @@ -36,51 +37,32 @@ public class SwitchDensityRule extends AbstractJavaRulechainRule { .build(); public SwitchDensityRule() { - super(ASTSwitchStatement.class); + super(ASTSwitchStatement.class, ASTSwitchExpression.class); definePropertyDescriptor(REPORT_LEVEL); } @Override public Object visit(ASTSwitchStatement node, Object data) { - double density = new SwitchDensityVisitor().compute(node); + return visitSwitchLike(node, data); + } + + @Override + public Object visit(ASTSwitchExpression node, Object data) { + return visitSwitchLike(node, data); + } + + public Void visitSwitchLike(ASTSwitchLike node, Object data) { + // note: this does not cross find boundaries. + int stmtCount = node.descendants(ASTStatement.class).count(); + int labelCount = node.getBranches() + .map(ASTSwitchBranch::getLabel) + .sumBy(label -> label.isDefault() ? 1 : label.getExprList().count()); + + // note: if labelCount is zero, double division will produce +Infinity or NaN, not ArithmeticException + double density = stmtCount / (double) labelCount; if (density >= getProperty(REPORT_LEVEL)) { addViolation(data, node); } - return super.visit(node, data); - } - - private static class SwitchDensityVisitor extends JavaParserVisitorAdapter { - - private int labels = 0; - private int stmts = 0; - private ASTSwitchStatement root; - - - double compute(ASTSwitchStatement root) { - this.root = root; - root.jjtAccept(this, null); - return labels == 0 ? 0 : ((double) stmts) / labels; - } - - - @Override - public Object visitStatement(ASTStatement statement, Object data) { - stmts++; - return super.visitStatement(statement, data); - } - - @Override - public Object visit(ASTExpression node, Object data) { - // don't recurse on anonymous class, etc - return data; - } - - @Override - public Object visit(ASTSwitchLabel switchLabel, Object data) { - if (switchLabel.getParent() == root) { - labels++; - } - return super.visit(switchLabel, data); - } + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UseUtilityClassRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UseUtilityClassRule.java index 672c6b56b8..abeeb9d7b7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UseUtilityClassRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UseUtilityClassRule.java @@ -6,98 +6,85 @@ package net.sourceforge.pmd.lang.java.rule.design; import static net.sourceforge.pmd.util.CollectionUtil.setOf; -import java.util.Collection; +import java.util.Set; -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; +import net.sourceforge.pmd.lang.java.ast.ASTBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMemberValuePair; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTResultType; -import net.sourceforge.pmd.lang.java.rule.AbstractLombokAwareRule; +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -public class UseUtilityClassRule extends AbstractLombokAwareRule { +public class UseUtilityClassRule extends AbstractJavaRulechainRule { - @Override - protected Collection defaultSuppressionAnnotations() { - return setOf("lombok.experimental.UtilityClass"); + private static final Set IGNORED_CLASS_ANNOT = setOf( + "lombok.experimental.UtilityClass", + "org.junit.runner.RunWith" // for suites and such + ); + + public UseUtilityClassRule() { + super(ASTClassOrInterfaceDeclaration.class); } @Override - public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (hasIgnoredAnnotation(node)) { + public Object visit(ASTClassOrInterfaceDeclaration klass, Object data) { + if (JavaRuleUtil.hasAnyAnnotation(klass, IGNORED_CLASS_ANNOT) + || TypeTestUtil.isA("junit.framework.TestSuite", klass) // suite method is ok + || klass.isInterface() + || klass.isAbstract() + || klass.getSuperClassTypeNode() != null + || klass.getSuperInterfaceTypeNodes().nonEmpty() + ) { return data; } - return super.visit(node, data); - } - @Override - public Object visit(ASTClassOrInterfaceBody decl, Object data) { - Object result = super.visit(decl, data); - - if (decl.getParent() instanceof ASTClassOrInterfaceDeclaration) { - ASTClassOrInterfaceDeclaration parent = (ASTClassOrInterfaceDeclaration) decl.getParent(); - if (parent.isAbstract() || parent.isInterface() || parent.getSuperClassTypeNode() != null) { - return result; + boolean hasAnyMethods = false; + boolean hasNonPrivateCtor = false; + boolean hasAnyCtor = false; + for (ASTBodyDeclaration declaration : klass.getDeclarations()) { + if (declaration instanceof ASTFieldDeclaration + && !((ASTFieldDeclaration) declaration).isStatic()) { + return null; } - - if (hasLombokNoArgsConstructor(parent)) { - return result; - } - - int i = decl.getNumChildren(); - int methodCount = 0; - boolean isOK = false; - while (i > 0) { - Node p = decl.getChild(--i); - if (p.getNumChildren() == 0) { - continue; - } - Node n = skipAnnotations(p); - if (n instanceof ASTFieldDeclaration) { - if (!((ASTFieldDeclaration) n).isStatic()) { - isOK = true; - break; - } - } else if (n instanceof ASTConstructorDeclaration) { - if (((ASTConstructorDeclaration) n).isPrivate()) { - isOK = true; - break; - } - } else if (n instanceof ASTMethodDeclaration) { - ASTMethodDeclaration m = (ASTMethodDeclaration) n; - if (!m.isPrivate()) { - methodCount++; - } - if (!m.isStatic()) { - isOK = true; - break; - } - - // TODO use symbol table - if ("suite".equals(m.getName())) { - ASTResultType res = m.getResultType(); - ASTClassOrInterfaceType c = res.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - if (c != null && c.hasImageEqualTo("Test")) { - isOK = true; - break; - } - } + if (declaration instanceof ASTConstructorDeclaration) { + hasAnyCtor = true; + if (((ASTConstructorDeclaration) declaration).getVisibility() != Visibility.V_PRIVATE) { + hasNonPrivateCtor = true; } } - if (!isOK && methodCount > 0) { - addViolation(data, decl); + + if (declaration instanceof ASTMethodDeclaration) { + if (((ASTMethodDeclaration) declaration).getVisibility() != Visibility.V_PRIVATE) { + hasAnyMethods = true; + } + if (!((ASTMethodDeclaration) declaration).isStatic()) { + return null; + } } } - return result; + + // account for default ctor + hasNonPrivateCtor |= !hasAnyCtor + && klass.getVisibility() != Visibility.V_PRIVATE + && !hasLombokPrivateCtor(klass); + + + String message; + if (hasAnyMethods && hasNonPrivateCtor) { + message = "This utility class has a non-private constructor"; + addViolationWithMessage(data, klass, message); + } + return null; } - private boolean hasLombokNoArgsConstructor(ASTClassOrInterfaceDeclaration parent) { + private boolean hasLombokPrivateCtor(ASTClassOrInterfaceDeclaration parent) { // check if there's a lombok no arg private constructor, if so skip the rest of the rules return parent.getDeclaredAnnotations() @@ -105,18 +92,16 @@ public class UseUtilityClassRule extends AbstractLombokAwareRule { .flatMap(ASTAnnotation::getMembers) // to set the access level of a constructor in lombok, you set the access property on the annotation .filterMatching(ASTMemberValuePair::getName, "access") - .map(ASTMemberValuePair::getValue) // This is from the AccessLevel enum in Lombok // if the constructor is found and the accesslevel is private no need to check anything else - .any(it -> "PRIVATE".equals(it.getImage())); + .any(it -> isAccessToVarWithName(it.getValue(), "PRIVATE")); } - private Node skipAnnotations(Node p) { - int index = 0; - Node n = p.getChild(index++); - while (n instanceof ASTAnnotation && index < p.getNumChildren()) { - n = p.getChild(index++); + private static boolean isAccessToVarWithName(JavaNode node, String name) { + if (node instanceof ASTNamedReferenceExpr) { + return ((ASTNamedReferenceExpr) node).getName().equals(name); } - return n; + return false; } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java index 3b1269a1ee..7a9793ecf8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java @@ -6,37 +6,38 @@ package net.sourceforge.pmd.lang.java.rule.design; import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTResultType; +import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; import net.sourceforge.pmd.lang.java.ast.ASTStatement; +import net.sourceforge.pmd.lang.java.ast.ASTSuperExpression; import net.sourceforge.pmd.lang.java.ast.ASTThrowsList; import net.sourceforge.pmd.lang.java.ast.ASTType; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; +import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.types.JMethodSig; +import net.sourceforge.pmd.lang.java.types.OverloadSelectionResult; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; import net.sourceforge.pmd.properties.PropertyDescriptor; - /** * @author Romain Pelisse, bugfix for [ 1522517 ] False +: * UselessOverridingMethod @@ -73,21 +74,16 @@ public class UselessOverridingMethodRule extends AbstractJavaRule { // TODO: this method should be externalize into an utility class, shouldn't it ? private boolean isMethodResultType(ASTMethodDeclaration node, Class resultType) { - ASTResultType type = node.getResultType(); - if (type != null && type.getChild(0) instanceof ASTType) { - Class resolvedResultType = ((ASTType) type.getChild(0)).getType(); - return resultType.equals(resolvedResultType); - } - return false; + ASTType type = node.getResultTypeNode(); + return TypeTestUtil.isA(resultType, type); } // TODO: this method should be externalize into an utility class, shouldn't it ? private boolean isMethodThrowingType(ASTMethodDeclaration node, Class exceptionType) { @Nullable ASTThrowsList thrownExceptions = node.getThrowsList(); if (thrownExceptions != null) { - List names = thrownExceptions.findChildrenOfType(ASTName.class); - for (ASTName name : names) { - if (name.getType() != null && name.getType() == exceptionType) { + for (ASTClassOrInterfaceType type : thrownExceptions) { + if (TypeTestUtil.isA(exceptionType, type)) { return true; } } @@ -106,7 +102,7 @@ public class UselessOverridingMethodRule extends AbstractJavaRule { // Can skip abstract methods and methods whose only purpose is to // guarantee that the inherited method is not changed by finalizing // them. - if (node.isAbstract() || node.isFinal() || node.isNative() || node.isSynchronized()) { + if (node.getModifiers().hasAny(JModifier.ABSTRACT, JModifier.FINAL, JModifier.NATIVE, JModifier.SYNCHRONIZED)) { return super.visit(node, data); } // We can also skip the 'clone' method as they are generally @@ -117,107 +113,92 @@ public class UselessOverridingMethodRule extends AbstractJavaRule { } ASTBlock block = node.getBody(); - if (block == null) { - return super.visit(node, data); - } // Only process functions with one BlockStatement - if (block.getNumChildren() != 1 || block.findDescendantsOfType(ASTStatement.class).size() != 1) { + if (block.getNumChildren() != 1 || block.descendants(ASTStatement.class).count() != 1) { return super.visit(node, data); } - Node statement = block.getChild(0).getChild(0); - if (statement.getChild(0).getNumChildren() == 0) { - return data; // skips empty return statements - } - Node statementGrandChild = statement.getChild(0).getChild(0); - ASTPrimaryExpression primaryExpression; - - if (statementGrandChild instanceof ASTPrimaryExpression) { - primaryExpression = (ASTPrimaryExpression) statementGrandChild; - } else { - List primaryExpressions = statementGrandChild - .findChildrenOfType(ASTPrimaryExpression.class); - if (primaryExpressions.size() != 1) { - return super.visit(node, data); - } - primaryExpression = primaryExpressions.get(0); + Node statement = block.getChild(0); + if (statement.getNumChildren() == 0) { + return super.visit(node, data); // skips empty return statements } - ASTPrimaryPrefix primaryPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - if (!primaryPrefix.usesSuperModifier()) { + if (!getProperty(IGNORE_ANNOTATIONS_DESCRIPTOR) + && node.getDeclaredAnnotations().any(it -> !TypeTestUtil.isA(Override.class, it))) { return super.visit(node, data); } - List primarySuffixList = primaryExpression.findChildrenOfType(ASTPrimarySuffix.class); - if (primarySuffixList.size() != 2) { - // extra method call on result of super method - return super.visit(node, data); - } - - ASTPrimarySuffix primarySuffix = primarySuffixList.get(0); - if (!primarySuffix.hasImageEqualTo(node.getName())) { - return super.visit(node, data); - } - // Process arguments - primarySuffix = primarySuffixList.get(1); - ASTArguments arguments = (ASTArguments) primarySuffix.getChild(0); - ASTFormalParameters formalParameters = node.getFormalParameters(); - if (formalParameters.getNumChildren() != arguments.getNumChildren()) { - return super.visit(node, data); - } - - if (!getProperty(IGNORE_ANNOTATIONS_DESCRIPTOR) && node.getDeclaredAnnotations().any(it -> !TypeTestUtil.isA(Override.class, it))) { - return super.visit(node, data); - } - - // different number of args - if (arguments.size() != node.getArity()) { - return super.visit(node, data); - } - - if (arguments.size() > 0) { - ASTArgumentList argumentList = (ASTArgumentList) arguments.getChild(0); - for (int i = 0; i < argumentList.getNumChildren(); i++) { - Node expressionChild = argumentList.getChild(i).getChild(0); - if (!(expressionChild instanceof ASTPrimaryExpression) || expressionChild.getNumChildren() != 1) { - // The arguments are not simply passed through - return super.visit(node, data); - } - - ASTPrimaryExpression argumentPrimaryExpression = (ASTPrimaryExpression) expressionChild; - ASTPrimaryPrefix argumentPrimaryPrefix = (ASTPrimaryPrefix) argumentPrimaryExpression.getChild(0); - if (argumentPrimaryPrefix.getNumChildren() == 0) { - // The arguments are not simply passed through (using "this" for instance) - return super.visit(node, data); - } - Node argumentPrimaryPrefixChild = argumentPrimaryPrefix.getChild(0); - if (!(argumentPrimaryPrefixChild instanceof ASTName)) { - // The arguments are not simply passed through - return super.visit(node, data); - } - - ASTName argumentName = (ASTName) argumentPrimaryPrefixChild; - ASTFormalParameter formalParameter = (ASTFormalParameter) formalParameters.getChild(i); - ASTVariableDeclaratorId variableId = formalParameter.getFirstChildOfType(ASTVariableDeclaratorId.class); - if (!argumentName.hasImageEqualTo(variableId.getImage())) { - // The arguments are not simply passed through - return super.visit(node, data); - } + // merely calling super.foo() or returning super.foo() + ASTMethodCall superMethodCall = null; + if ((statement instanceof ASTExpressionStatement || statement instanceof ASTReturnStatement) + && statement.getNumChildren() == 1 + && statement.getChild(0) instanceof ASTMethodCall) { + ASTMethodCall methodCall = (ASTMethodCall) statement.getChild(0); + if (methodCall.getQualifier() instanceof ASTSuperExpression) { + superMethodCall = methodCall; } } - if (modifiersChanged(node)) { + if (superMethodCall == null) { + return super.visit(node, data); + } + + if (!isSuperCallSameMethod(node, superMethodCall)) { + return super.visit(node, data); + } + + if (modifiersChanged(node, superMethodCall)) { return super.visit(node, data); } // All arguments are passed through directly or there were no arguments - addViolation(data, node, getMessage()); + addViolation(data, node); return super.visit(node, data); } + private boolean isSuperCallSameMethod(ASTMethodDeclaration node, ASTMethodCall methodCall) { + @NonNull + ASTFormalParameters formalParameters = node.getFormalParameters(); + @NonNull + ASTArgumentList arguments = methodCall.getArguments(); + + OverloadSelectionResult overloadSelectionInfo = methodCall.getOverloadSelectionInfo(); + JMethodSig methodType = overloadSelectionInfo.getMethodType(); + + if (node.getName().equals(methodCall.getMethodName()) + && formalParameters.size() == arguments.size()) { + // simple case - no args + if (formalParameters.size() == 0) { + return true; + } + + // compare each arg + for (int i = 0; i < node.getArity(); i++) { + ASTFormalParameter formalParam = formalParameters.get(i); + ASTExpression arg = arguments.get(i); + + if (!(arg instanceof ASTVariableAccess)) { + return false; + } + ASTVariableAccess varAccess = (ASTVariableAccess) arg; + if (!formalParam.getVarId().getName().equals(varAccess.getName())) { + return false; + } + // check the type - must be equal for overwrites, but could be different for overloads + if (!formalParam.getTypeMirror().equals(methodType.getFormalParameters().get(i))) { + return false; + } + } + + // now all args matched + return true; + } + return false; + } + private boolean isCloneMethod(ASTMethodDeclaration node) { - boolean isCloneAndPublic = CLONE_METHOD_NAME.equals(node.getName()) && node.isPublic(); + boolean isCloneAndPublic = CLONE_METHOD_NAME.equals(node.getName()) && node.getVisibility() == Visibility.V_PUBLIC; boolean hasNoParameters = node.getArity() == 0; return isCloneAndPublic && hasNoParameters @@ -225,53 +206,18 @@ public class UselessOverridingMethodRule extends AbstractJavaRule { && this.isMethodThrowingType(node, CloneNotSupportedException.class); } - private boolean modifiersChanged(ASTMethodDeclaration node) { - Class type = node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class).getType(); - if (type == null) { - return false; - } - - String overriddenMethodName = node.getName(); - - List> typeArguments = new ArrayList<>(); - for (ASTFormalParameter parameter : node.getFormalParameters()) { - Class parameterType = parameter.getType(); - if (parameterType != null) { - typeArguments.add(parameterType); - } - } - - // did we have for each parameter the type? - if (typeArguments.size() != node.getFormalParameters().size()) { - return false; - } - - // search method with same name up the hierarchy - Class[] typeArgumentArray = typeArguments.toArray(new Class[0]); - Class superType = type.getSuperclass(); - Method declaredMethod = null; - while (superType != null && declaredMethod == null) { - try { - declaredMethod = superType.getDeclaredMethod(overriddenMethodName, typeArgumentArray); - } catch (NoSuchMethodException | SecurityException e) { - declaredMethod = null; - } - superType = superType.getSuperclass(); - } - + private boolean modifiersChanged(ASTMethodDeclaration node, ASTMethodCall superMethodCall) { + JMethodSig declaredMethod = superMethodCall.getOverloadSelectionInfo().getMethodType(); return declaredMethod != null && isElevatingAccessModifier(node, declaredMethod); } - private boolean isElevatingAccessModifier(ASTMethodDeclaration overridingMethod, Method superMethod) { - String superPackageName = null; - Package p = superMethod.getDeclaringClass().getPackage(); - if (p != null) { - superPackageName = p.getName(); - } + private boolean isElevatingAccessModifier(ASTMethodDeclaration overridingMethod, JMethodSig superMethod) { + String superPackageName = superMethod.getDeclaringType().getSymbol().getPackageName(); + // Note: can't simply compare superMethod.getModifiers() with overridingMethod.getModifiers() // since AccessNode#PROTECTED != Modifier#PROTECTED. boolean elevatingFromProtected = Modifier.isProtected(superMethod.getModifiers()) - && !overridingMethod.isProtected(); + && overridingMethod.getVisibility() != Visibility.V_PROTECTED; boolean elevatingFromPackagePrivate = superMethod.getModifiers() == 0 && !overridingMethod.getModifiers().getExplicitModifiers().isEmpty(); boolean elevatingIntoDifferentPackage = !packageName.equals(superPackageName) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticRule.java index 9c3f01631e..70f8fbc542 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticRule.java @@ -4,59 +4,53 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.AccessType; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; -import net.sourceforge.pmd.lang.java.ast.AccessNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; +import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess; +import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; /** * @author Eric Olander * @since Created on October 24, 2004, 8:56 AM */ -public class AssignmentToNonFinalStaticRule extends AbstractJavaRule { +public class AssignmentToNonFinalStaticRule extends AbstractJavaRulechainRule { - @Override - public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - Map> vars = node.getScope() - .getDeclarations(VariableNameDeclaration.class); - for (Map.Entry> entry : vars.entrySet()) { - VariableNameDeclaration decl = entry.getKey(); - AccessNode accessNodeParent = decl.getAccessNodeParent(); - if (!accessNodeParent.isStatic() || accessNodeParent.isFinal()) { - continue; - } - - final List locations = initializedInConstructor(entry.getValue()); - for (final Node location : locations) { - addViolation(data, location, decl.getImage()); - } - } - return super.visit(node, data); + public AssignmentToNonFinalStaticRule() { + super(ASTFieldAccess.class, ASTVariableAccess.class); } - private List initializedInConstructor(List usages) { - final List unsafeAssignments = new ArrayList<>(); - for (NameOccurrence occ : usages) { - // specifically omitting prefix and postfix operators as there are - // legitimate usages of these with static fields, e.g. typesafe enum pattern. - if (((JavaNameOccurrence) occ).isOnLeftHandSide()) { - Node node = occ.getLocation(); - Node constructor = node.getFirstParentOfType(ASTConstructorDeclaration.class); - if (constructor != null) { - unsafeAssignments.add(node); + @Override + public Object visit(ASTVariableAccess node, Object data) { + checkAccess(node, data); + return null; + } + + @Override + public Object visit(ASTFieldAccess node, Object data) { + checkAccess(node, data); + return null; + } + + private void checkAccess(ASTNamedReferenceExpr node, Object data) { + if (isInsideConstructor(node) && node.getAccessType() == AccessType.WRITE) { + @Nullable + JVariableSymbol symbol = node.getReferencedSym(); + if (symbol != null && symbol.isField()) { + JFieldSymbol field = (JFieldSymbol) symbol; + if (field.isStatic() && !field.isFinal()) { + addViolation(data, node, field.getSimpleName()); } } } - - return unsafeAssignments; } + private boolean isInsideConstructor(ASTNamedReferenceExpr node) { + return node.ancestors(ASTConstructorDeclaration.class).nonEmpty(); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidBranchingStatementAsLastInLoopRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidBranchingStatementAsLastInLoopRule.java index b7348aa26f..a8dc537728 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidBranchingStatementAsLastInLoopRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidBranchingStatementAsLastInLoopRule.java @@ -13,19 +13,20 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTBreakStatement; import net.sourceforge.pmd.lang.java.ast.ASTContinueStatement; import net.sourceforge.pmd.lang.java.ast.ASTDoStatement; import net.sourceforge.pmd.lang.java.ast.ASTForStatement; +import net.sourceforge.pmd.lang.java.ast.ASTForeachStatement; import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement; import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; -import net.sourceforge.pmd.properties.PropertySource; -public class AvoidBranchingStatementAsLastInLoopRule extends AbstractJavaRule { +public class AvoidBranchingStatementAsLastInLoopRule extends AbstractJavaRulechainRule { public static final String CHECK_FOR = "for"; public static final String CHECK_DO = "do"; @@ -53,20 +54,17 @@ public class AvoidBranchingStatementAsLastInLoopRule extends AbstractJavaRule { public AvoidBranchingStatementAsLastInLoopRule() { + super(ASTBreakStatement.class, ASTContinueStatement.class, ASTReturnStatement.class); definePropertyDescriptor(CHECK_BREAK_LOOP_TYPES); definePropertyDescriptor(CHECK_CONTINUE_LOOP_TYPES); definePropertyDescriptor(CHECK_RETURN_LOOP_TYPES); - - addRuleChainVisit(ASTBreakStatement.class); - addRuleChainVisit(ASTContinueStatement.class); - addRuleChainVisit(ASTReturnStatement.class); } @Override public Object visit(ASTBreakStatement node, Object data) { // skip breaks, that are within a switch statement - if (node.getNthParent(3) instanceof ASTSwitchStatement) { + if (node.ancestors().get(1) instanceof ASTSwitchStatement) { return data; } return check(CHECK_BREAK_LOOP_TYPES, node, data); @@ -74,8 +72,11 @@ public class AvoidBranchingStatementAsLastInLoopRule extends AbstractJavaRule { protected Object check(PropertyDescriptor> property, Node node, Object data) { - Node parent = node.getNthParent(5); - if (parent instanceof ASTForStatement) { + Node parent = node.getParent(); + if (parent instanceof ASTBlock) { + parent = parent.getParent(); + } + if (parent instanceof ASTForStatement || parent instanceof ASTForeachStatement) { if (hasPropertyValue(property, CHECK_FOR)) { super.addViolation(data, node); } @@ -109,9 +110,6 @@ public class AvoidBranchingStatementAsLastInLoopRule extends AbstractJavaRule { } - /** - * @see PropertySource#dysfunctionReason() - */ @Override public String dysfunctionReason() { return checksNothing() ? "All loop types are ignored" : null; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidUsingOctalValuesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidUsingOctalValuesRule.java index c91705bfe2..d2e70ff639 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidUsingOctalValuesRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidUsingOctalValuesRule.java @@ -6,39 +6,36 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty; -import java.util.regex.Pattern; - -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.properties.PropertyDescriptor; -public class AvoidUsingOctalValuesRule extends AbstractJavaRule { - - public static final Pattern OCTAL_PATTERN = Pattern.compile("0[0-7]{2,}[lL]?"); - - public static final Pattern STRICT_OCTAL_PATTERN = Pattern.compile("0[0-7]+[lL]?"); - - private static final PropertyDescriptor STRICT_METHODS_DESCRIPTOR = booleanProperty("strict") - .desc("Detect violations between 00 and 07") - .defaultValue(false) - .build(); +public class AvoidUsingOctalValuesRule extends AbstractJavaRulechainRule { + private static final PropertyDescriptor STRICT_METHODS_DESCRIPTOR = + booleanProperty("strict") + .desc("Detect violations between 00 and 07") + .defaultValue(false) + .build(); public AvoidUsingOctalValuesRule() { + super(ASTNumericLiteral.class); definePropertyDescriptor(STRICT_METHODS_DESCRIPTOR); } @Override - public Object visit(ASTLiteral node, Object data) { - boolean strict = getProperty(STRICT_METHODS_DESCRIPTOR); - Pattern p = strict ? STRICT_OCTAL_PATTERN : OCTAL_PATTERN; - - String img = node.getImage(); - if (img != null && p.matcher(img).matches()) { - addViolation(data, node); + public Object visit(ASTNumericLiteral node, Object data) { + if (node.getBase() == 8) { + if (getProperty(STRICT_METHODS_DESCRIPTOR) || !isBetweenZeroAnd7(node)) { + addViolation(data, node); + } } + return null; + } - return data; + private boolean isBetweenZeroAnd7(ASTNumericLiteral node) { + long value = node.getConstValue().longValue(); + return 0 <= value && value <= 7; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CheckSkipResultRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CheckSkipResultRule.java index 3b5710f780..b806f108ec 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CheckSkipResultRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CheckSkipResultRule.java @@ -4,56 +4,28 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.io.InputStream; +import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.InvocationMatcher; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; +public class CheckSkipResultRule extends AbstractJavaRulechainRule { -public class CheckSkipResultRule extends AbstractJavaRule { + private static final InvocationMatcher SKIP_METHOD = InvocationMatcher.parse("java.io.InputStream#skip(_*)"); + + public CheckSkipResultRule() { + super(ASTMethodCall.class); + } @Override - public Object visit(ASTVariableDeclaratorId node, Object data) { - if (!TypeTestUtil.isA(InputStream.class, node.getTypeNode())) { - return data; + public Object visit(ASTMethodCall call, Object data) { + if (SKIP_METHOD.matchesCall(call) && !isResultUsed(call)) { + addViolation(data, call); } - for (NameOccurrence occ : node.getUsages()) { - JavaNameOccurrence jocc = (JavaNameOccurrence) occ; - NameOccurrence qualifier = jocc.getNameForWhichThisIsAQualifier(); - if (qualifier != null && "skip".equals(qualifier.getImage())) { - Node loc = jocc.getLocation(); - if (loc != null) { - ASTPrimaryExpression exp = loc.getFirstParentOfType(ASTPrimaryExpression.class); - while (exp != null) { - if (exp.getParent() instanceof ASTStatementExpression) { - // if exp is in a bare statement, - // the returned value is not used - addViolation(data, occ.getLocation()); - break; - } else if (exp.getParent() instanceof ASTExpression - && exp.getParent().getParent() instanceof ASTPrimaryPrefix) { - // if exp is enclosed in a pair of parenthesis - // let's have a look at the enclosing expression - // we'll see if it's in a bare statement - exp = exp.getFirstParentOfType(ASTPrimaryExpression.class); - } else { - // if exp is neither in a bare statement - // or between a pair of parentheses, - // it's in some other kind of statement - // or assignment so the returned value is used - break; - } - } - } - } - } - return data; + return null; + } + + private boolean isResultUsed(ASTMethodCall call) { + return !(call.getParent() instanceof ASTExpressionStatement); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java index db98024bda..4d0ed46550 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -7,49 +7,50 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty; import static net.sourceforge.pmd.properties.PropertyFactory.stringListProperty; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; + import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator; +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; +import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression; import net.sourceforge.pmd.lang.java.ast.ASTBlock; -import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; import net.sourceforge.pmd.lang.java.ast.ASTFinallyClause; +import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; -import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTResourceList; import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; +import net.sourceforge.pmd.lang.java.ast.ASTStatement; import net.sourceforge.pmd.lang.java.ast.ASTTryStatement; +import net.sourceforge.pmd.lang.java.ast.ASTType; +import net.sourceforge.pmd.lang.java.ast.ASTTypeExpression; +import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.ast.Annotatable; import net.sourceforge.pmd.lang.java.ast.BinaryOp; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.TypeNode; +import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; +import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; +import net.sourceforge.pmd.lang.java.types.InvocationMatcher; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -71,9 +72,9 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; */ public class CloseResourceRule extends AbstractJavaRule { - private static final String WRAPPING_TRY_WITH_RES_VAR_MESSAGE = "it is recommended to wrap resource in try-with-resource declaration directly"; - private static final String REASSIGN_BEFORE_CLOSED_MESSAGE = "'' is reassigned, but the original instance is not closed"; - private static final String CLOSE_IN_FINALLY_BLOCK_MESSAGE = "'' is not closed within a finally block, thus might not be closed at all in case of exceptions"; + private static final String WRAPPING_TRY_WITH_RES_VAR_MESSAGE = "it is recommended to wrap resource ''{0}'' in try-with-resource declaration directly"; + private static final String REASSIGN_BEFORE_CLOSED_MESSAGE = "''{0}'' is reassigned, but the original instance is not closed"; + private static final String CLOSE_IN_FINALLY_BLOCK_MESSAGE = "''{0}'' is not closed within a finally block, thus might not be closed at all in case of exceptions"; private static final PropertyDescriptor> CLOSE_TARGETS_DESCRIPTOR = stringListProperty("closeTargets") @@ -110,8 +111,6 @@ public class CloseResourceRule extends AbstractJavaRule { // keeps track of already reported violations to avoid duplicated violations for the same variable private final Set reportedVarNames = new HashSet<>(); - private boolean hasStaticImportObjectsNonNull; - public CloseResourceRule() { definePropertyDescriptor(CLOSE_TARGETS_DESCRIPTOR); definePropertyDescriptor(TYPES_DESCRIPTOR); @@ -122,8 +121,6 @@ public class CloseResourceRule extends AbstractJavaRule { @Override public void start(RuleContext ctx) { - hasStaticImportObjectsNonNull = false; - closeTargets.clear(); simpleTypes.clear(); types.clear(); @@ -165,70 +162,86 @@ public class CloseResourceRule extends AbstractJavaRule { private void checkForResources(ASTMethodOrConstructorDeclaration methodOrConstructor, Object data) { reportedVarNames.clear(); - Map resVars = getResourceVariables(methodOrConstructor); - for (Map.Entry resVarEntry : resVars.entrySet()) { - ASTVariableDeclarator resVar = resVarEntry.getKey(); - TypeNode resVarType = resVarEntry.getValue(); + Map resVars = getResourceVariables(methodOrConstructor); + for (Map.Entry resVarEntry : resVars.entrySet()) { + ASTVariableDeclaratorId resVar = resVarEntry.getKey(); + TypeNode runtimeType = resVarEntry.getValue(); + TypeNode resVarType = wrappedResourceTypeOrReturn(resVar, runtimeType); + if (isWrappingResourceSpecifiedInTry(resVar)) { - reportedVarNames.add(resVar.getVarId().getName()); - addViolationWithMessage(data, resVar, WRAPPING_TRY_WITH_RES_VAR_MESSAGE); + reportedVarNames.add(resVar.getName()); + addViolationWithMessage(data, resVar, WRAPPING_TRY_WITH_RES_VAR_MESSAGE, + new Object[] { resVar.getName() }); } else if (shouldVarOfTypeBeClosedInMethod(resVar, resVarType, methodOrConstructor)) { - reportedVarNames.add(resVar.getVarId().getName()); - addCloseResourceViolation(resVar.getVarId(), resVarType, data); + reportedVarNames.add(resVar.getName()); + addCloseResourceViolation(resVar, runtimeType, data); } else if (isNotAllowedResourceType(resVarType)) { - ASTStatementExpression reassigningStatement = getFirstReassigningStatementBeforeBeingClosed(resVar, methodOrConstructor); + ASTExpressionStatement reassigningStatement = getFirstReassigningStatementBeforeBeingClosed(resVar, methodOrConstructor); if (reassigningStatement != null) { - reportedVarNames.add(resVar.getVarId().getName()); - addViolationWithMessage(data, reassigningStatement, reassignBeforeClosedMessageForVar(resVar.getName())); + reportedVarNames.add(resVar.getName()); + addViolationWithMessage(data, reassigningStatement, REASSIGN_BEFORE_CLOSED_MESSAGE, + new Object[] { resVar.getName() }); } } } } - private Map getResourceVariables(ASTMethodOrConstructorDeclaration method) { - List vars = method.findDescendantsOfType(ASTVariableDeclarator.class); - Map resVars = new HashMap<>(); - for (ASTVariableDeclarator var : vars) { - if (var.getParent() instanceof Annotatable - && ((Annotatable) var.getParent()).isAnnotationPresent("lombok.Cleanup")) { - continue; // auto cleaned up - } + private Map getResourceVariables(ASTMethodOrConstructorDeclaration method) { + Map resVars = new HashMap<>(); + + if (method.getBody() == null) { + return resVars; + } + + List vars = method.getBody().descendants(ASTVariableDeclaratorId.class) + .filterNot(ASTVariableDeclaratorId::isFormalParameter) + .filterNot(ASTVariableDeclaratorId::isExceptionBlockParameter) + .filter(this::isVariableNotSpecifiedInTryWithResource) + .filter(var -> isResourceTypeOrSubtype(var) || isNodeInstanceOfResourceType(getTypeOfVariable(var))) + .filter(var -> var.getAnnotation("lombok.Cleanup") == null) + .toList(); + + for (ASTVariableDeclaratorId var : vars) { TypeNode varType = getTypeOfVariable(var); - if (varType != null && isResourceTypeOrSubtype(varType)) { - resVars.put(var, wrappedResourceTypeOrReturn(var, varType)); - } + resVars.put(var, varType); } return resVars; } - private TypeNode getTypeOfVariable(ASTVariableDeclarator var) { + private TypeNode getTypeOfVariable(ASTVariableDeclaratorId var) { TypeNode runtimeType = getRuntimeTypeOfVariable(var); return runtimeType != null ? runtimeType : getDeclaredTypeOfVariable(var); } - private TypeNode getDeclaredTypeOfVariable(ASTVariableDeclarator var) { - ASTLocalVariableDeclaration localVar = (ASTLocalVariableDeclaration) var.getParent(); + private TypeNode getDeclaredTypeOfVariable(ASTVariableDeclaratorId var) { + ASTLocalVariableDeclaration localVar = (ASTLocalVariableDeclaration) var.getParent().getParent(); return localVar.getTypeNode(); // note: can be null, if type is inferred (var) } - private TypeNode getRuntimeTypeOfVariable(ASTVariableDeclarator var) { - ASTExpression initExpr = initializerExpressionOf(var); + private TypeNode getRuntimeTypeOfVariable(ASTVariableDeclaratorId var) { + ASTExpression initExpr = var.getInitializer(); return isRuntimeType(initExpr) ? initExpr : null; } private boolean isRuntimeType(ASTExpression expr) { - return expr != null && isNotMethodCall(expr) && expr.getType() != null; + if (expr == null || isMethodCall(expr) || expr instanceof ASTNullLiteral) { + return false; + } + + @Nullable + JTypeDeclSymbol symbol = expr.getTypeMirror().getSymbol(); + return symbol != null && !symbol.isUnresolved(); } - private TypeNode wrappedResourceTypeOrReturn(ASTVariableDeclarator var, TypeNode defaultVal) { + private TypeNode wrappedResourceTypeOrReturn(ASTVariableDeclaratorId var, TypeNode defaultVal) { TypeNode wrappedResType = getWrappedResourceType(var); return wrappedResType != null ? wrappedResType : defaultVal; } - private TypeNode getWrappedResourceType(ASTVariableDeclarator var) { + private TypeNode getWrappedResourceType(ASTVariableDeclaratorId var) { ASTExpression initExpr = initializerExpressionOf(var); if (initExpr != null) { - ASTAllocationExpression resAlloc = getLastResourceAllocation(initExpr); + ASTConstructorCall resAlloc = getLastResourceAllocation(initExpr); if (resAlloc != null) { ASTExpression firstArgRes = getFirstArgumentVariableIfResource(resAlloc); return firstArgRes != null ? firstArgRes : resAlloc; @@ -237,17 +250,15 @@ public class CloseResourceRule extends AbstractJavaRule { return null; } - private ASTExpression initializerExpressionOf(ASTVariableDeclarator var) { - return var.hasInitializer() - ? var.getInitializer().getFirstChildOfType(ASTExpression.class) - : null; + private ASTExpression initializerExpressionOf(ASTVariableDeclaratorId var) { + return var.getInitializer(); } - private ASTAllocationExpression getLastResourceAllocation(ASTExpression expr) { - List allocations = expr.findDescendantsOfType(ASTAllocationExpression.class); + private ASTConstructorCall getLastResourceAllocation(ASTExpression expr) { + List allocations = expr.descendantsOrSelf().filterIs(ASTConstructorCall.class).toList(); int lastAllocIndex = allocations.size() - 1; for (int allocIndex = lastAllocIndex; allocIndex >= 0; allocIndex--) { - ASTAllocationExpression allocation = allocations.get(allocIndex); + ASTConstructorCall allocation = allocations.get(allocIndex); if (isResourceTypeOrSubtype(allocation)) { return allocation; } @@ -255,11 +266,11 @@ public class CloseResourceRule extends AbstractJavaRule { return null; } - private ASTExpression getFirstArgumentVariableIfResource(ASTAllocationExpression allocation) { - ASTArgumentList argsList = allocation.getFirstDescendantOfType(ASTArgumentList.class); - if (argsList != null) { - ASTExpression firstArg = argsList.getFirstChildOfType(ASTExpression.class); - return firstArg != null && isNotMethodCall(firstArg) && isResourceTypeOrSubtype(firstArg) + private ASTExpression getFirstArgumentVariableIfResource(ASTConstructorCall allocation) { + ASTArgumentList argsList = allocation.getArguments(); + if (argsList != null && argsList.size() > 0) { + ASTExpression firstArg = argsList.get(0); + return isNotMethodCall(firstArg) && isResourceTypeOrSubtype(firstArg) ? firstArg : null; } @@ -271,27 +282,26 @@ public class CloseResourceRule extends AbstractJavaRule { } private boolean isMethodCall(ASTExpression expression) { - if (expression != null) { - ASTPrimaryExpression primaryExpression = expression.getFirstChildOfType(ASTPrimaryExpression.class); - return primaryExpression != null && primaryExpression.getFirstChildOfType(ASTPrimarySuffix.class) != null; - } - return false; + return expression instanceof ASTMethodCall; } - private boolean isWrappingResourceSpecifiedInTry(ASTVariableDeclarator var) { - String wrappedVarName = getWrappedVariableName(var); + private boolean isWrappingResourceSpecifiedInTry(ASTVariableDeclaratorId var) { + ASTVariableAccess wrappedVarName = getWrappedVariableName(var); if (wrappedVarName != null) { - List tryContainers = var.getParentsOfType(ASTTryStatement.class); - for (ASTTryStatement tryContainer : tryContainers) { - if (isTryWithResourceSpecifyingVariable(tryContainer, wrappedVarName)) { - return true; + ASTVariableDeclaratorId referencedVar = wrappedVarName.getReferencedSym().tryGetNode(); + if (referencedVar != null) { + List tryContainers = referencedVar.ancestors(ASTTryStatement.class).toList(); + for (ASTTryStatement tryContainer : tryContainers) { + if (isTryWithResourceSpecifyingVariable(tryContainer, referencedVar)) { + return true; + } } } } return false; } - private boolean shouldVarOfTypeBeClosedInMethod(ASTVariableDeclarator var, TypeNode type, + private boolean shouldVarOfTypeBeClosedInMethod(ASTVariableDeclaratorId var, TypeNode type, ASTMethodOrConstructorDeclaration method) { return isNotAllowedResourceType(type) && isNotWrappingResourceMethodParameter(var, method) && isResourceVariableUnclosed(var); @@ -315,44 +325,46 @@ public class CloseResourceRule extends AbstractJavaRule { return false; } - private boolean isNotWrappingResourceMethodParameter(ASTVariableDeclarator var, + private boolean isNotWrappingResourceMethodParameter(ASTVariableDeclaratorId var, ASTMethodOrConstructorDeclaration method) { return !isWrappingResourceMethodParameter(var, method); } /** - * Checks whether the variable is resource and initialized from a method parameter. + * Checks whether the variable is a resource and initialized from a method parameter. * @param var the resource variable that is being initialized * @param method the method or constructor in which the variable is declared - * @return true if the variable is resource and initialized from a method parameter. false + * @return true if the variable is a resource and initialized from a method parameter. false * otherwise. */ - private boolean isWrappingResourceMethodParameter(ASTVariableDeclarator var, ASTMethodOrConstructorDeclaration method) { - String wrappedVarName = getWrappedVariableName(var); + private boolean isWrappingResourceMethodParameter(ASTVariableDeclaratorId var, ASTMethodOrConstructorDeclaration method) { + ASTVariableAccess wrappedVarName = getWrappedVariableName(var); if (wrappedVarName != null) { - ASTFormalParameters methodParams = method.getFirstDescendantOfType(ASTFormalParameters.class); - if (methodParams != null) { - List ids = methodParams.findDescendantsOfType(ASTVariableDeclaratorId.class); - for (ASTVariableDeclaratorId id : ids) { - if (id.hasImageEqualTo(wrappedVarName) && isResourceTypeOrSubtype(id)) { - return true; - } + ASTFormalParameters methodParams = method.getFormalParameters(); + for (ASTFormalParameter param : methodParams) { + if ((isResourceTypeOrSubtype(param) || wrappedVarName.getParent() instanceof ASTVariableDeclarator + || wrappedVarName.getParent() instanceof ASTAssignmentExpression) + && JavaRuleUtil.isReferenceToVar(wrappedVarName, param.getVarId().getSymbol())) { + return true; } } } return false; } - private String getWrappedVariableName(ASTVariableDeclarator var) { - if (var.hasInitializer()) { - ASTName varName = var.getInitializer().getFirstDescendantOfType(ASTName.class); - return varName != null ? varName.getImage() : null; + private ASTVariableAccess getWrappedVariableName(ASTVariableDeclaratorId var) { + ASTExpression initializer = var.getInitializer(); + if (initializer != null) { + return var.getInitializer().descendantsOrSelf().filterIs(ASTVariableAccess.class) + .filter(usage -> !(usage.getParent() instanceof ASTMethodCall)).first(); } return null; } private boolean isResourceTypeOrSubtype(TypeNode refType) { - return refType.getType() != null + @Nullable + JTypeDeclSymbol symbol = refType.getTypeMirror().getSymbol(); + return symbol != null && !symbol.isUnresolved() ? isNodeInstanceOfResourceType(refType) : nodeHasReferenceToResourceType(refType); } @@ -367,8 +379,13 @@ public class CloseResourceRule extends AbstractJavaRule { } private boolean nodeHasReferenceToResourceType(TypeNode refType) { - ASTClassOrInterfaceType type = refType.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - return type != null && isResourceTypeName(type.getImage()) && !type.isReferenceToClassSameCompilationUnit(); + @Nullable + JTypeDeclSymbol symbol = refType.getTypeMirror().getSymbol(); + if (symbol != null) { + String simpleTypeName = symbol.getSimpleName(); + return isResourceTypeName(simpleTypeName); + } + return false; } private boolean isResourceTypeName(String typeName) { @@ -376,14 +393,14 @@ public class CloseResourceRule extends AbstractJavaRule { return types.contains(typeName) || simpleTypes.contains(simpleTypeName); } - private boolean isResourceVariableUnclosed(ASTVariableDeclarator var) { + private boolean isResourceVariableUnclosed(ASTVariableDeclaratorId var) { return !isResourceVariableClosed(var); } - private boolean isResourceVariableClosed(ASTVariableDeclarator var) { + private boolean isResourceVariableClosed(ASTVariableDeclaratorId var) { Node methodOfVar = getMethodOfNode(var); return hasTryStatementClosingResourceVariable(methodOfVar, var) - || isReturnedByMethod(var.getName(), methodOfVar); + || isReturnedByMethod(var, methodOfVar); } private Node getMethodOfNode(Node node) { @@ -398,8 +415,8 @@ public class CloseResourceRule extends AbstractJavaRule { return !(node instanceof ASTBlock || node instanceof ASTConstructorDeclaration); } - private boolean hasTryStatementClosingResourceVariable(Node node, ASTVariableDeclarator var) { - List tryStatements = node.findDescendantsOfType(ASTTryStatement.class, true); + private boolean hasTryStatementClosingResourceVariable(Node node, ASTVariableDeclaratorId var) { + List tryStatements = node.descendants(ASTTryStatement.class).crossFindBoundaries().toList(); for (ASTTryStatement tryStatement : tryStatements) { if (tryStatementClosesResourceVariable(tryStatement, var)) { return true; @@ -408,28 +425,27 @@ public class CloseResourceRule extends AbstractJavaRule { return false; } - private boolean tryStatementClosesResourceVariable(ASTTryStatement tryStatement, ASTVariableDeclarator var) { + private boolean tryStatementClosesResourceVariable(ASTTryStatement tryStatement, ASTVariableDeclaratorId var) { if (tryStatement.getBeginLine() >= var.getBeginLine() && noneCriticalStatementsBetween(var, tryStatement)) { - if (isTryWithResourceSpecifyingVariable(tryStatement, var.getName())) { + if (isTryWithResourceSpecifyingVariable(tryStatement, var)) { return true; } if (hasFinallyClause(tryStatement)) { ASTBlock finallyBody = tryStatement.getFinallyClause().getBody(); - return blockClosesResourceVariable(finallyBody, var.getName()); + return blockClosesResourceVariable(finallyBody, var); } } return false; } - private boolean noneCriticalStatementsBetween(ASTVariableDeclarator var, ASTTryStatement tryStatement) { + private boolean noneCriticalStatementsBetween(ASTVariableDeclaratorId var, ASTTryStatement tryStatement) { return !anyCriticalStatementBetween(var, tryStatement); } - private boolean anyCriticalStatementBetween(ASTVariableDeclarator var, ASTTryStatement tryStatement) { - ASTBlockStatement varBlockStatement = var.getFirstParentOfType(ASTBlockStatement.class); - ASTBlockStatement tryBlockStatement = tryStatement.getFirstParentOfType(ASTBlockStatement.class); - if (isNotNullInitialized(var) && areStatementsOfSameBlock(varBlockStatement, tryBlockStatement)) { - for (ASTBlockStatement bsBetween : getBlockStatementsBetween(varBlockStatement, tryBlockStatement)) { + private boolean anyCriticalStatementBetween(ASTVariableDeclaratorId var, ASTTryStatement tryStatement) { + ASTStatement varStatement = var.ancestors(ASTStatement.class).first(); + if (isNotNullInitialized(var) && areStatementsOfSameBlock(varStatement, tryStatement)) { + for (ASTStatement bsBetween : getBlockStatementsBetween(varStatement, tryStatement)) { if (isCriticalStatement(bsBetween)) { return true; } @@ -438,86 +454,90 @@ public class CloseResourceRule extends AbstractJavaRule { return false; } - private boolean isNotNullInitialized(ASTVariableDeclarator var) { + private boolean isNotNullInitialized(ASTVariableDeclaratorId var) { return !hasNullInitializer(var); } - private boolean hasNullInitializer(ASTVariableDeclarator var) { - if (var.hasInitializer()) { - ASTPrimaryPrefix primaryPrefix = var.getInitializer().getFirstDescendantOfType(ASTPrimaryPrefix.class); - return primaryPrefix != null && primaryPrefix.hasDescendantOfType(ASTNullLiteral.class); - } - return false; + private boolean hasNullInitializer(ASTVariableDeclaratorId var) { + return var.getInitializer() instanceof ASTNullLiteral; } - private boolean areStatementsOfSameBlock(ASTBlockStatement bs0, ASTBlockStatement bs1) { + private boolean areStatementsOfSameBlock(ASTStatement bs0, ASTStatement bs1) { return bs0.getParent() == bs1.getParent(); } - private List getBlockStatementsBetween(ASTBlockStatement top, ASTBlockStatement bottom) { - List blockStatements = top.getParent().findChildrenOfType(ASTBlockStatement.class); - int topBSIndex = blockStatements.indexOf(top); - int bottomBSIndex = blockStatements.indexOf(bottom); - return blockStatements.subList(topBSIndex + 1, bottomBSIndex); + private List getBlockStatementsBetween(ASTStatement top, ASTStatement bottom) { + List blockStatements = top.getParent().children(ASTStatement.class).toList(); + int topIndex = blockStatements.indexOf(top); + int bottomIndex = blockStatements.indexOf(bottom); + return blockStatements.subList(topIndex + 1, bottomIndex); } - private boolean isCriticalStatement(ASTBlockStatement blockStatement) { - boolean isVarDeclaration = blockStatement.hasDescendantOfType(ASTLocalVariableDeclaration.class); - boolean isAssignmentOperator = blockStatement.hasDescendantOfType(ASTAssignmentOperator.class); + private boolean isCriticalStatement(ASTStatement blockStatement) { + boolean isVarDeclaration = blockStatement.descendantsOrSelf().filterIs(ASTLocalVariableDeclaration.class).nonEmpty(); + boolean isAssignmentOperator = blockStatement.descendantsOrSelf().filterIs(ASTAssignmentExpression.class).nonEmpty(); return !isVarDeclaration && !isAssignmentOperator; } - private boolean isTryWithResourceSpecifyingVariable(ASTTryStatement tryStatement, String varName) { - return tryStatement.isTryWithResources() && isVariableSpecifiedInTryWithResource(varName, tryStatement); + private boolean isTryWithResourceSpecifyingVariable(ASTTryStatement tryStatement, ASTVariableDeclaratorId varId) { + return tryStatement.isTryWithResources() && isVariableSpecifiedInTryWithResource(varId, tryStatement); } - private boolean isVariableSpecifiedInTryWithResource(String varName, ASTTryStatement tryWithResource) { - List specifiedResources = getResourcesSpecifiedInTryWith(tryWithResource); - for (JavaNode res : specifiedResources) { - if (res.hasImageEqualTo(varName)) { + private boolean isVariableNotSpecifiedInTryWithResource(ASTVariableDeclaratorId varId) { + @Nullable + ASTTryStatement tryStatement = varId.ancestors(ASTTryStatement.class) + .filter(ASTTryStatement::isTryWithResources) + .first(); + return tryStatement == null || !isVariableSpecifiedInTryWithResource(varId, tryStatement); + } + + private boolean isVariableSpecifiedInTryWithResource(ASTVariableDeclaratorId varId, ASTTryStatement tryWithResource) { + // skip own resources - these are definitively closed + if (tryWithResource.getResources().descendants(ASTVariableDeclaratorId.class).toList().contains(varId)) { + return true; + } + + List usedVars = getResourcesSpecifiedInTryWith(tryWithResource); + for (ASTVariableAccess res : usedVars) { + if (JavaRuleUtil.isReferenceToVar(res, varId.getSymbol())) { return true; } } return false; } - private List getResourcesSpecifiedInTryWith(ASTTryStatement tryWithResource) { - ASTResourceList resSpecification = tryWithResource.getFirstChildOfType(ASTResourceList.class); - List initializedVars = resSpecification - .findDescendantsOfType(ASTVariableDeclaratorId.class); - List specifiedVars = resSpecification.findDescendantsOfType(ASTName.class); - return combineNodeLists(initializedVars, specifiedVars); - } - - private List combineNodeLists(List list0, List list1) { - List nodeList = new ArrayList<>(list0); - nodeList.addAll(list1); - return nodeList; + private List getResourcesSpecifiedInTryWith(ASTTryStatement tryWithResource) { + return tryWithResource.getResources().descendantsOrSelf().filterIs(ASTVariableAccess.class).toList(); } private boolean hasFinallyClause(ASTTryStatement tryStatement) { return tryStatement.getFinallyClause() != null; } - private boolean blockClosesResourceVariable(ASTBlock block, String variableToClose) { + private boolean blockClosesResourceVariable(ASTBlock block, ASTVariableDeclaratorId variableToClose) { return hasNotConditionalCloseCallOnVariable(block, variableToClose) || hasMethodCallClosingResourceVariable(block, variableToClose); } - private boolean hasNotConditionalCloseCallOnVariable(ASTBlock block, String variableToClose) { - List operations = block.findDescendantsOfType(ASTName.class); - for (ASTName operation : operations) { - if (isCloseCallOnVariable(operation, variableToClose) - && isNotConditional(block, operation, variableToClose)) { + private boolean hasNotConditionalCloseCallOnVariable(ASTBlock block, ASTVariableDeclaratorId variableToClose) { + List methodCallsOnVariable = block.descendants(ASTMethodCall.class) + .filter(call -> isMethodCallOnVariable(call, variableToClose)) + .toList(); + + for (ASTMethodCall call : methodCallsOnVariable) { + if (isCloseTargetMethodCall(call) && isNotConditional(block, call, variableToClose)) { return true; } } return false; } - - private boolean isCloseCallOnVariable(ASTName op, String variableToClose) { - String closedVar = getVariableClosedByMethodCall(op); - return variableToClose.equals(closedVar); + + private boolean isMethodCallOnVariable(ASTExpression expr, ASTVariableDeclaratorId variable) { + if (expr instanceof ASTMethodCall) { + ASTMethodCall methodCall = (ASTMethodCall) expr; + return JavaRuleUtil.isReferenceToVar(methodCall.getQualifier(), variable.getSymbol()); + } + return false; } /** @@ -533,168 +553,108 @@ public class CloseResourceRule extends AbstractJavaRule { * @return true if no if condition is involved or if the if * condition is a null-check. */ - private boolean isNotConditional(ASTBlock enclosingBlock, Node node, String varName) { + private boolean isNotConditional(ASTBlock enclosingBlock, Node node, ASTVariableDeclaratorId var) { ASTIfStatement ifStatement = findIfStatement(enclosingBlock, node); if (ifStatement != null) { // find expressions like: varName != null or null != varName - // Expression/EqualityExpression[@Image='!='] - // [PrimaryExpression/PrimaryPrefix/Name[@Image='" + varName + "']] - // [PrimaryExpression/PrimaryPrefix/Literal/NullLiteral] - ASTInfixExpression equalityExpr = ifStatement.getCondition().getFirstChildOfType(ASTInfixExpression.class); - if (equalityExpr != null && BinaryOp.NE == equalityExpr.getOperator()) { - JavaNode left = equalityExpr.getChild(0); - JavaNode right = equalityExpr.getChild(1); - - if (isVariableAccess(left, varName) && isNullLiteral(right) - || isVariableAccess(right, varName) && isNullLiteral(left)) { - return true; + if (ifStatement.getCondition() instanceof ASTInfixExpression) { + ASTInfixExpression equalityExpr = (ASTInfixExpression) ifStatement.getCondition(); + if (BinaryOp.NE == equalityExpr.getOperator()) { + ASTExpression left = equalityExpr.getLeftOperand(); + ASTExpression right = equalityExpr.getRightOperand(); + + if (JavaRuleUtil.isReferenceToVar(left, var.getSymbol()) && isNullLiteral(right) + || JavaRuleUtil.isReferenceToVar(right, var.getSymbol()) && isNullLiteral(left)) { + return true; + } } } // find method call Objects.nonNull(varName) - if (isMethodCall(ifStatement.getCondition())) { - ASTPrimaryExpression methodCall = ifStatement.getCondition().getFirstChildOfType(ASTPrimaryExpression.class); - ASTPrimaryPrefix prefix = methodCall.getFirstChildOfType(ASTPrimaryPrefix.class); - ASTName methodName = prefix.getFirstChildOfType(ASTName.class); - if (isObjectsNonNull(methodName)) { - ASTArgumentList arguments = methodCall.getFirstChildOfType(ASTPrimarySuffix.class) - .getFirstDescendantOfType(ASTArgumentList.class); - if (arguments.size() == 1) { - JavaNode firstArgument = arguments.getChild(0); - if (firstArgument.getNumChildren() > 0) { - return isVariableAccess(firstArgument.getChild(0), varName); - } - } - } - } - - return false; + return isObjectsNonNull(ifStatement.getCondition(), var); } return true; } - @Override - public Object visit(ASTImportDeclaration node, Object data) { - if (node.isStatic()) { - if ("java.util.Objects".equals(node.getImportedName()) && node.isImportOnDemand() - || "java.util.Objects.nonNull".equals(node.getImportedName()) && !node.isImportOnDemand()) { - hasStaticImportObjectsNonNull = true; - } - } - return super.visit(node, data); - } - - private boolean isObjectsNonNull(ASTName methodName) { - if (methodName == null) { - return false; - } - if (methodName.hasImageEqualTo("Objects.nonNull")) { - return methodName.getType() == Objects.class; - } - if (methodName.hasImageEqualTo("nonNull")) { - return hasStaticImportObjectsNonNull; + private boolean isObjectsNonNull(ASTExpression expression, ASTVariableDeclaratorId var) { + InvocationMatcher matcher = InvocationMatcher.parse("java.util.Objects#nonNull(_)"); + if (matcher.matchesCall(expression)) { + ASTMethodCall methodCall = (ASTMethodCall) expression; + return JavaRuleUtil.isReferenceToVar(methodCall.getArguments().get(0), var.getSymbol()); } return false; } - private boolean isVariableAccess(JavaNode node, String varName) { - if (node == null || node.getNumChildren() < 1 || node.getChild(0).getNumChildren() < 1) { - return false; - } - - return node instanceof ASTPrimaryExpression && node.getChild(0) instanceof ASTPrimaryPrefix - && node.getChild(0).getChild(0) instanceof ASTName - && node.getChild(0).getChild(0).hasImageEqualTo(varName); - } - private boolean isNullLiteral(JavaNode node) { - if (node == null || node.getNumChildren() < 1 || node.getChild(0).getNumChildren() < 1) { - return false; - } - - return node instanceof ASTPrimaryExpression && node.getChild(0) instanceof ASTPrimaryPrefix - && node.getChild(0).getChild(0) instanceof ASTLiteral - && node.getChild(0).getChild(0).getFirstChildOfType(ASTNullLiteral.class) != null; + return node instanceof ASTNullLiteral; } private ASTIfStatement findIfStatement(ASTBlock enclosingBlock, Node node) { - ASTIfStatement ifStatement = node.getFirstParentOfType(ASTIfStatement.class); - List allIfStatements = enclosingBlock.findDescendantsOfType(ASTIfStatement.class); + ASTIfStatement ifStatement = node.ancestors(ASTIfStatement.class).first(); + List allIfStatements = enclosingBlock.descendants(ASTIfStatement.class).toList(); if (ifStatement != null && allIfStatements.contains(ifStatement)) { return ifStatement; } return null; } - private boolean hasMethodCallClosingResourceVariable(ASTBlock block, String variableToClose) { - List expressions = block.findDescendantsOfType(ASTPrimaryExpression.class, true); - for (ASTPrimaryExpression expression : expressions) { - if (isMethodCallClosingResourceVariable(expression, variableToClose)) { + private boolean hasMethodCallClosingResourceVariable(ASTBlock block, ASTVariableDeclaratorId variableToClose) { + List methodCalls = block.descendants(ASTMethodCall.class).crossFindBoundaries().toList(); + for (ASTMethodCall call : methodCalls) { + if (isMethodCallClosingResourceVariable(call, variableToClose)) { return true; } } return false; } - private boolean isMethodCallClosingResourceVariable(ASTPrimaryExpression expression, String variableToClose) { - ASTPrimaryPrefix prefix = expression.getFirstDescendantOfType(ASTPrimaryPrefix.class); - ASTPrimarySuffix suffix = expression.getFirstDescendantOfType(ASTPrimarySuffix.class); - if (prefix != null && suffix != null) { - return (isCloseTargetMethodCall(prefix, suffix) || hasChainedCloseTargetMethodCall(expression)) - && variableIsPassedToMethod(variableToClose, expression); + private boolean isMethodCallClosingResourceVariable(ASTExpression expr, ASTVariableDeclaratorId variableToClose) { + if (!(expr instanceof ASTMethodCall)) { + return false; + } + ASTMethodCall call = (ASTMethodCall) expr; + return (isCloseTargetMethodCall(call) || hasChainedCloseTargetMethodCall(call)) + && variableIsPassedToMethod(variableToClose, call); + } + + private boolean isCloseTargetMethodCall(ASTMethodCall methodCall) { + String fullName = methodCall.getMethodName(); + if (methodCall.getQualifier() instanceof ASTTypeExpression) { + fullName = methodCall.getQualifier().getText() + "." + fullName; + } + return closeTargets.contains(fullName); + } + + private boolean hasChainedCloseTargetMethodCall(ASTMethodCall start) { + ASTExpression walker = start; + while (walker instanceof ASTMethodCall) { + ASTMethodCall methodCall = (ASTMethodCall) walker; + if (isCloseTargetMethodCall(methodCall)) { + return true; + } + walker = methodCall.getQualifier(); } return false; } - private boolean isCloseTargetMethodCall(ASTPrimaryPrefix prefix, ASTPrimarySuffix suffix) { - String methodCall = getMethodCallStr(prefix, suffix); - return methodCall != null && closeTargets.contains(methodCall); - } - - private String getMethodCallStr(ASTPrimaryPrefix prefix, ASTPrimarySuffix suffix) { - if (prefix.getImage() == null) { - ASTName name = prefix.getFirstDescendantOfType(ASTName.class); - return name != null ? name.getImage() : null; - } else if (suffix.getImage() != null) { - return prefix.getImage() + "." + suffix.getImage(); - } - return null; - } - - private boolean hasChainedCloseTargetMethodCall(ASTPrimaryExpression expr) { - List methodCalls = expr.findDescendantsOfType(ASTPrimarySuffix.class, true); - for (ASTPrimarySuffix methodCall : methodCalls) { - if (closeTargets.contains(methodCall.getImage())) { + private boolean variableIsPassedToMethod(ASTVariableDeclaratorId varName, ASTMethodCall methodCall) { + List usedRefs = methodCall.getArguments().descendants(ASTNamedReferenceExpr.class).toList(); + for (ASTNamedReferenceExpr ref : usedRefs) { + if (varName.getSymbol().equals(ref.getReferencedSym())) { return true; } } return false; } - private boolean variableIsPassedToMethod(String varName, ASTPrimaryExpression methodCall) { - List methodCallArgs = methodCall.findDescendantsOfType(ASTName.class, true); - for (ASTName methodCallArg : methodCallArgs) { - if (isMethodCallArgument(methodCallArg) && methodCallArg.hasImageEqualTo(varName)) { - return true; - } - } - return false; - } - - private boolean isMethodCallArgument(ASTName varName) { - return varName.getFirstParentOfType(ASTArgumentList.class) != null; - } - - private boolean isReturnedByMethod(String varName, Node method) { - List returns = method.findDescendantsOfType(ASTReturnStatement.class, true); - for (ASTReturnStatement returnStatement : returns) { - ASTName name = returnStatement.getFirstDescendantOfType(ASTName.class); - if (name != null && name.hasImageEqualTo(varName)) { - return true; - } - } - return false; + private boolean isReturnedByMethod(ASTVariableDeclaratorId variable, Node method) { + return method + .descendants(ASTReturnStatement.class).crossFindBoundaries() + .descendants(ASTVariableAccess.class) + .filter(access -> !(access.getParent() instanceof ASTMethodCall)) + .filter(access -> JavaRuleUtil.isReferenceToVar(access, variable.getSymbol())) + .nonEmpty(); } private void addCloseResourceViolation(ASTVariableDeclaratorId id, TypeNode type, Object data) { @@ -703,75 +663,59 @@ public class CloseResourceRule extends AbstractJavaRule { } private String getResourceTypeName(ASTVariableDeclaratorId varId, TypeNode type) { - Class typeClass = type.getType(); - if (typeClass == null) { - ASTLocalVariableDeclaration localVarDecl = varId.getFirstParentOfType(ASTLocalVariableDeclaration.class); - return localVarDecl != null && localVarDecl.getTypeNode() != null - ? localVarDecl.getTypeNode().getTypeImage() - : varId.getName(); + if (type instanceof ASTType) { + return PrettyPrintingUtil.prettyPrintType((ASTType) type); } - return typeClass.getSimpleName(); + @Nullable + JTypeDeclSymbol symbol = type.getTypeMirror().getSymbol(); + if (symbol != null) { + return symbol.getSimpleName(); + } + @Nullable + ASTLocalVariableDeclaration localVarDecl = varId.ancestors(ASTLocalVariableDeclaration.class).first(); + if (localVarDecl != null && localVarDecl.getTypeNode() != null) { + return PrettyPrintingUtil.prettyPrintType(localVarDecl.getTypeNode()); + } + return varId.getName(); } @Override - public Object visit(ASTPrimaryPrefix prefix, Object data) { + public Object visit(ASTMethodCall node, Object data) { if (!getProperty(DETECT_CLOSE_NOT_IN_FINALLY)) { - return super.visit(prefix, data); + return super.visit(node, data); } - ASTName methodCall = prefix.getFirstChildOfType(ASTName.class); - if (methodCall != null && isNodeInstanceOfResourceType(methodCall)) { - String closedVar = getVariableClosedByMethodCall(methodCall); - if (closedVar != null && isNotInFinallyBlock(prefix) && !reportedVarNames.contains(closedVar)) { - String violationMsg = closeInFinallyBlockMessageForVar(closedVar); - addViolationWithMessage(data, prefix, violationMsg); + if (isCloseTargetMethodCall(node) && node.getQualifier() instanceof ASTVariableAccess) { + ASTVariableAccess closedVar = (ASTVariableAccess) node.getQualifier(); + if (isNotInFinallyBlock(closedVar) && !reportedVarNames.contains(closedVar.getName())) { + addViolationWithMessage(data, closedVar, CLOSE_IN_FINALLY_BLOCK_MESSAGE, + new Object[] { closedVar.getName() }); } } - return super.visit(prefix, data); + + return super.visit(node, data); } - private String getVariableClosedByMethodCall(ASTName methodCall) { - String[] callParts = getMethodCallParts(methodCall); - if (callParts != null) { - String varName = callParts[0]; - String methodName = callParts[1]; - return closeTargets.contains(methodName) ? varName : null; - } - return null; + private boolean isNotInFinallyBlock(ASTVariableAccess closedVar) { + return closedVar.ancestors(ASTFinallyClause.class).isEmpty(); } - private String[] getMethodCallParts(ASTName methodCall) { - String methodCallStr = methodCall.getImage(); - return methodCallStr != null && methodCallStr.contains(".") - ? methodCallStr.split("\\.") - : null; - } - - private boolean isNotInFinallyBlock(ASTPrimaryPrefix prefix) { - return prefix.getFirstParentOfType(ASTFinallyClause.class) == null; - } - - private String closeInFinallyBlockMessageForVar(String var) { - return "''" + var + CLOSE_IN_FINALLY_BLOCK_MESSAGE; - } - - private String reassignBeforeClosedMessageForVar(String var) { - return "''" + var + REASSIGN_BEFORE_CLOSED_MESSAGE; - } - - private ASTStatementExpression getFirstReassigningStatementBeforeBeingClosed(ASTVariableDeclarator variable, ASTMethodOrConstructorDeclaration methodOrConstructor) { - List statements = methodOrConstructor.findDescendantsOfType(ASTStatementExpression.class); + private ASTExpressionStatement getFirstReassigningStatementBeforeBeingClosed(ASTVariableDeclaratorId variable, ASTMethodOrConstructorDeclaration methodOrConstructor) { + List statements = methodOrConstructor.descendants(ASTExpressionStatement.class).toList(); boolean variableClosed = false; boolean isInitialized = !hasNullInitializer(variable); ASTExpression initializingExpression = initializerExpressionOf(variable); - for (ASTStatementExpression statement : statements) { + for (ASTExpressionStatement statement : statements) { if (isClosingVariableStatement(statement, variable)) { variableClosed = true; } if (isAssignmentForVariable(statement, variable)) { + ASTAssignmentExpression assignment = (ASTAssignmentExpression) statement.getFirstChild(); if (isInitialized && !variableClosed) { - if (initializingExpression != null && !inSameIfBlock(statement, initializingExpression)) { + if (initializingExpression != null && !inSameIfBlock(statement, initializingExpression) + && notInNullCheckIf(statement, variable) + && isNotSelfAssignment(assignment)) { return statement; } } @@ -781,51 +725,48 @@ public class CloseResourceRule extends AbstractJavaRule { } if (!isInitialized) { isInitialized = true; - initializingExpression = statement.getFirstDescendantOfType(ASTExpression.class); - } + initializingExpression = statement.getExpr(); + } } } return null; } - private boolean inSameIfBlock(ASTStatementExpression statement1, ASTExpression statement2) { - List parents1 = statement1.getParentsOfType(ASTIfStatement.class); - List parents2 = statement2.getParentsOfType(ASTIfStatement.class); + private boolean isNotSelfAssignment(ASTAssignmentExpression assignment) { + return assignment.getRightOperand().descendantsOrSelf().filterIs(ASTVariableAccess.class).filter(access -> { + return JavaRuleUtil.isReferenceToSameVar(access, assignment.getLeftOperand()); + }).isEmpty(); + } + + private boolean notInNullCheckIf(ASTExpressionStatement statement, ASTVariableDeclaratorId variable) { + Node grandparent = statement.ancestors().get(1); + if (grandparent instanceof ASTIfStatement) { + ASTIfStatement ifStatement = (ASTIfStatement) grandparent; + if (JavaRuleUtil.isNullCheck(ifStatement.getCondition(), variable.getSymbol())) { + return false; + } + } + return true; + } + + private boolean inSameIfBlock(ASTExpressionStatement statement1, ASTExpression statement2) { + List parents1 = statement1.ancestors(ASTIfStatement.class).toList(); + List parents2 = statement2.ancestors(ASTIfStatement.class).toList(); parents1.retainAll(parents2); return !parents1.isEmpty(); } - private boolean isClosingVariableStatement(ASTStatementExpression statement, ASTVariableDeclarator variable) { - List expressions = statement.findDescendantsOfType(ASTPrimaryExpression.class); - for (ASTPrimaryExpression expression : expressions) { - if (isMethodCallClosingResourceVariable(expression, variable.getName())) { - return true; - } - } - List names = statement.findDescendantsOfType(ASTName.class); - for (ASTName name : names) { - if (isCloseCallOnVariable(name, variable.getName())) { - return true; - } - } - return false; + private boolean isClosingVariableStatement(ASTExpressionStatement statement, ASTVariableDeclaratorId variable) { + return isMethodCallClosingResourceVariable(statement.getExpr(), variable) + || isMethodCallOnVariable(statement.getExpr(), variable); } - private boolean isAssignmentForVariable(ASTStatementExpression statement, ASTVariableDeclarator variable) { - if (statement == null || variable == null) { + private boolean isAssignmentForVariable(ASTExpressionStatement statement, ASTVariableDeclaratorId variable) { + if (statement == null || variable == null || !(statement.getExpr() instanceof ASTAssignmentExpression)) { return false; } - List assignments = statement.findDescendantsOfType(ASTAssignmentOperator.class); - for (ASTAssignmentOperator assignment : assignments) { - // The sibling before the operator is the left hand side - JavaNode lhs = assignment.getParent().getChild(assignment.getIndexInParent() - 1); - - if (isVariableAccess(lhs, variable.getName())) { - return true; - } - } - - return false; + ASTAssignmentExpression assignment = (ASTAssignmentExpression) statement.getExpr(); + return JavaRuleUtil.isReferenceToVar(assignment.getLeftOperand(), variable.getSymbol()); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontImportSunRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontImportSunRule.java deleted file mode 100644 index e5581f70d2..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontImportSunRule.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class DontImportSunRule extends AbstractJavaRule { - - @Override - public Object visit(ASTImportDeclaration node, Object data) { - String img = node.getChild(0).getImage(); - if (img.startsWith("sun.") && !img.startsWith("sun.misc.Signal")) { - addViolation(data, node); - } - return data; - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/IdempotentOperationsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/IdempotentOperationsRule.java index 7fe32a7ab7..8909e215bb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/IdempotentOperationsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/IdempotentOperationsRule.java @@ -4,82 +4,23 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.List; +import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression; +import net.sourceforge.pmd.lang.java.ast.AssignmentOp; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +public class IdempotentOperationsRule extends AbstractJavaRulechainRule { -public class IdempotentOperationsRule extends AbstractJavaRule { + public IdempotentOperationsRule() { + super(ASTAssignmentExpression.class); + } @Override - public Object visit(ASTStatementExpression node, Object data) { - if (node.getNumChildren() != 3 || !(node.getChild(0) instanceof ASTPrimaryExpression) - || !(node.getChild(1) instanceof ASTAssignmentOperator) - || ((ASTAssignmentOperator) node.getChild(1)).isCompound() - || !(node.getChild(2) instanceof ASTExpression) - || node.getChild(0).getChild(0).getNumChildren() == 0 - || node.getChild(2).getChild(0).getChild(0).getNumChildren() == 0) { - return super.visit(node, data); + public Object visit(ASTAssignmentExpression node, Object data) { + if (node.getOperator() == AssignmentOp.ASSIGN + && JavaRuleUtil.isReferenceToSameVar(node.getLeftOperand(), node.getRightOperand())) { + addViolation(data, node); } - - Node lhs = node.getChild(0).getChild(0).getChild(0); - if (!(lhs instanceof ASTName)) { - return super.visit(node, data); - } - - Node rhs = node.getChild(2).getChild(0).getChild(0).getChild(0); - if (!(rhs instanceof ASTName)) { - return super.visit(node, data); - } - - if (!lhs.hasImageEqualTo(rhs.getImage())) { - return super.visit(node, data); - } - - if (lhs.getParent().getParent().getNumChildren() > 1) { - Node n = lhs.getParent().getParent().getChild(1); - if (n instanceof ASTPrimarySuffix && ((ASTPrimarySuffix) n).isArrayDereference()) { - return super.visit(node, data); - } - } - - if (rhs.getParent().getParent().getNumChildren() > 1) { - Node n = rhs.getParent().getParent().getChild(1); - if (n instanceof ASTPrimarySuffix && ((ASTPrimarySuffix) n).isArguments() - || ((ASTPrimarySuffix) n).isArrayDereference()) { - return super.visit(node, data); - } - } - - if (lhs.findDescendantsOfType(ASTPrimarySuffix.class).size() != rhs - .findDescendantsOfType(ASTPrimarySuffix.class).size()) { - return super.visit(node, data); - } - - List lhsSuffixes = lhs.getParent().getParent() - .findDescendantsOfType(ASTPrimarySuffix.class); - List rhsSuffixes = rhs.getParent().getParent() - .findDescendantsOfType(ASTPrimarySuffix.class); - if (lhsSuffixes.size() != rhsSuffixes.size()) { - return super.visit(node, data); - } - - for (int i = 0; i < lhsSuffixes.size(); i++) { - ASTPrimarySuffix l = lhsSuffixes.get(i); - ASTPrimarySuffix r = rhsSuffixes.get(i); - - if (!l.hasImageEqualTo(r.getImage())) { - return super.visit(node, data); - } - } - - addViolation(data, node); - return super.visit(node, data); + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingBreakInSwitchRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughRule.java similarity index 94% rename from pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingBreakInSwitchRule.java rename to pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughRule.java index 16c30d2e23..148db33b6e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingBreakInSwitchRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughRule.java @@ -18,15 +18,14 @@ import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass; import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass.DataflowResult; import net.sourceforge.pmd.util.OptionalBool; -public class MissingBreakInSwitchRule extends AbstractJavaRulechainRule { +public class ImplicitSwitchFallThroughRule extends AbstractJavaRulechainRule { //todo should consider switch exprs - // todo rename to ImplicitSwitchFallThrough private static final Pattern IGNORED_COMMENT = Pattern.compile("/[/*].*\\bfalls?[ -]?thr(ough|u)\\b.*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE); - public MissingBreakInSwitchRule() { + public ImplicitSwitchFallThroughRule() { super(ASTSwitchStatement.class); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatRule.java index f577f891db..bc07fb8599 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatRule.java @@ -4,339 +4,133 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import static net.sourceforge.pmd.lang.ast.NodeStream.asInstanceOf; +import static net.sourceforge.pmd.util.CollectionUtil.immutableSetOf; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.OptionalInt; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.NodeStream; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTArrayInitializer; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTEnumBody; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTInitializer; -import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer; -import net.sourceforge.pmd.lang.java.ast.TypeNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.symboltable.NameDeclaration; +import org.checkerframework.checker.nullness.qual.NonNull; -public class InvalidLogMessageFormatRule extends AbstractJavaRule { +import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; +import net.sourceforge.pmd.lang.java.ast.ASTArrayAllocation; +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass; +import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass.AssignmentEntry; +import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass.DataflowResult; +import net.sourceforge.pmd.lang.java.rule.internal.DataflowPass.ReachingDefinitionSet; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.util.CollectionUtil; + +public class InvalidLogMessageFormatRule extends AbstractJavaRulechainRule { /** * Finds placeholder for ParameterizedMessages and format specifiers * for StringFormattedMessages. */ - private static final Pattern PLACEHOLDER_AND_FORMAT_SPECIFIER = - Pattern.compile("(\\{\\})|(%(?:\\d\\$)?(?:\\w+)?(?:\\d+)?(?:\\.\\d+)?\\w)"); + private static final Pattern PLACEHOLDER_AND_FORMAT_SPECIFIER = + Pattern.compile("(\\{})|(%(?:\\d\\$)?(?:\\w+)?(?:\\d+)?(?:\\.\\d+)?\\w)"); - private static final Map> LOGGERS; + private static final Set SLF4J = immutableSetOf("trace", "debug", "info", "warn", "error"); + private static final Set APACHE_SLF4J = immutableSetOf("trace", "debug", "info", "warn", "error", "fatal", "all"); - static { - Map> loggersMap = new HashMap<>(); - - loggersMap.put("org.slf4j.Logger", Collections - .unmodifiableSet(new HashSet<>(Arrays.asList("trace", "debug", "info", "warn", "error")))); - loggersMap.put("org.apache.logging.log4j.Logger", Collections - .unmodifiableSet(new HashSet<>(Arrays.asList("trace", "debug", "info", "warn", "error", "fatal", "all")))); - - LOGGERS = loggersMap; - } - - private boolean formatIsStringFormat; public InvalidLogMessageFormatRule() { - addRuleChainVisit(ASTImportDeclaration.class); - addRuleChainVisit(ASTName.class); + super(ASTMethodCall.class); } @Override - public void start(RuleContext ctx) { - formatIsStringFormat = false; - } + public Object visit(ASTMethodCall call, Object data) { + if (isLoggerCall(call, "org.slf4j.Logger", SLF4J) + || isLoggerCall(call, "org.apache.logging.log4j.Logger", APACHE_SLF4J)) { - @Override - public Object visit(ASTImportDeclaration node, Object data) { - if (node.isStatic()) { - if ("java.lang.String.format".equals(node.getImportedName())) { - formatIsStringFormat = true; + ASTArgumentList args = call.getArguments(); + ASTExpression messageParam = args.toStream().first(it -> TypeTestUtil.isA(String.class, it)); + if (messageParam == null) { + return null; } - if ("java.lang.String".equals(node.getImportedName()) && node.isImportOnDemand()) { - formatIsStringFormat = true; + + OptionalInt expectedArgs = expectedArguments0(messageParam); + if (!expectedArgs.isPresent()) { + // ignore if we couldn't analyze the message parameter + return null; } - } - return data; - } + int expectedArguments = expectedArgs.getAsInt(); - @Override - public Object visit(final ASTName node, final Object data) { - final NameDeclaration nameDeclaration = node.getNameDeclaration(); - // ignore imports or methods - if (!(nameDeclaration instanceof VariableNameDeclaration)) { - return data; - } - final String loggingClass; - // ignore unsupported logger - Class type = ((VariableNameDeclaration) nameDeclaration).getType(); - if (type == null || !LOGGERS.containsKey(type.getName())) { - return data; - } else { - loggingClass = type.getName(); - } + int providedArguments = args.size() - (messageParam.getIndexInParent() + 1); - // get the node that contains the logger - final ASTPrimaryExpression parentNode = node.getFirstParentOfType(ASTPrimaryExpression.class); - - // get the log level - final String method = parentNode.getFirstChildOfType(ASTPrimaryPrefix.class).getFirstChildOfType(ASTName.class) - .getImage().replace(nameDeclaration.getImage() + ".", ""); - - // ignore if not a log level - if (!LOGGERS.get(loggingClass).contains(method)) { - return data; - } - - // find the arguments - final List argumentList = new ArrayList<>(parentNode.getFirstChildOfType(ASTPrimarySuffix.class) - .getFirstDescendantOfType(ASTArgumentList.class).findChildrenOfType(ASTExpression.class)); - - if (argumentList.get(0).getType() != null && !argumentList.get(0).getType().equals(String.class)) { - if (argumentList.size() == 1) { - // no need to check for message params in case no string and no params found - return data; - } else { - // ignore the first argument if it is a known non-string value, e.g. a slf4j-Marker - argumentList.remove(0); + if (providedArguments == 1 && JavaRuleUtil.isArrayInitializer(args.getLastChild())) { + providedArguments = ((ASTArrayAllocation) args.getLastChild()).getArrayInitializer().length(); + } else if (TypeTestUtil.isA(Throwable.class, args.getLastChild()) + && providedArguments > expectedArguments) { + // Remove throwable param, since it is shown separately. + // But only, if it is not used as a placeholder argument + providedArguments--; } - } - // remove the message parameter - final ASTExpression messageParam = argumentList.remove(0); - - // ignore if String.format - if (isStringFormatCall(messageParam)) { - return data; - } - - final int expectedArguments = expectedArguments(messageParam); - if (expectedArguments == -1) { - // ignore if we couldn't analyze the message parameter - return data; - } - - // Remove throwable param, since it is shown separately. - // But only, if it is not used as a placeholder argument - if (argumentList.size() > expectedArguments) { - removeThrowableParam(argumentList); - } - - int providedArguments = argumentList.size(); - - // last argument could be an array with parameters - if (argumentList.size() == 1 && TypeTestUtil.isA("java.lang.Object[]", argumentList.get(0))) { - ASTArrayInitializer arrayInitializer = argumentList.get(0).getFirstDescendantOfType(ASTArrayInitializer.class); - if (arrayInitializer != null) { - providedArguments = arrayInitializer.getNumChildren(); + if (providedArguments < expectedArguments) { + addViolationWithMessage( + data, call, + "Missing arguments," + getExpectedMessage(providedArguments, expectedArguments)); + } else if (providedArguments > expectedArguments) { + addViolationWithMessage( + data, call, + "Too many arguments," + getExpectedMessage(providedArguments, expectedArguments)); } + } - if (providedArguments < expectedArguments) { - addViolationWithMessage(data, node, - "Missing arguments," + getExpectedMessage(argumentList, expectedArguments)); - } else if (providedArguments > expectedArguments) { - addViolationWithMessage(data, node, - "Too many arguments," + getExpectedMessage(argumentList, expectedArguments)); - } - - return data; + return null; } - private boolean isNewThrowable(ASTPrimaryExpression last) { - // in case a new exception is created or the exception class is - // mentioned. - return TypeTestUtil.isA(Throwable.class, last.getFirstDescendantOfType(ASTClassOrInterfaceType.class)); + private boolean isLoggerCall(ASTMethodCall call, String loggerType, Set methodNames) { + return TypeTestUtil.isA(loggerType, call.getQualifier()) && methodNames.contains(call.getMethodName()); } - private boolean hasTypeThrowable(TypeNode last) { - // if the type could be determined already - return last.getType() != null && TypeTestUtil.isA(Throwable.class, last); - } - - private boolean isReferencingThrowable(ASTPrimaryExpression last) { - // check the variable type, if there is a reference by name - ASTName variable = last.getFirstDescendantOfType(ASTName.class); - if (variable != null && variable.getNameDeclaration() != null - && variable.getNameDeclaration() instanceof VariableNameDeclaration) { - VariableNameDeclaration declaration = (VariableNameDeclaration) variable.getNameDeclaration(); - if (declaration.getType() != null && Throwable.class.isAssignableFrom(declaration.getType())) { - return true; - } - // convention: Exception type names should end with Exception - if (declaration.getTypeImage() != null && declaration.getTypeImage().endsWith("Exception")) { - return true; - } - } - return false; - } - - private void removeThrowableParam(final List params) { - // Throwable parameters are the last one in the list, if any. - if (params.isEmpty()) { - return; - } - int lastIndex = params.size() - 1; - ASTExpression lastExpression = params.get(lastIndex); - ASTPrimaryExpression last = lastExpression.getFirstDescendantOfType(ASTPrimaryExpression.class); - - if (isNewThrowable(last) || hasTypeThrowable(lastExpression) || isReferencingThrowable(last) || isLambdaParameter(last)) { - params.remove(lastIndex); - } - } - - private boolean isLambdaParameter(ASTPrimaryExpression last) { - String varName = null; - ASTPrimaryPrefix prefix = last.getFirstChildOfType(ASTPrimaryPrefix.class); - if (prefix != null) { - ASTName name = prefix.getFirstChildOfType(ASTName.class); - if (name != null) { - varName = name.getImage(); - } - } - for (NameDeclaration decl : prefix.getScope().getDeclarations().keySet()) { - if (decl.getName().equals(varName)) { - if (decl.getNode().getParent() instanceof ASTLambdaExpression) { - // If the last parameter is a lambda parameter, then we also ignore it - regardless of the type. - // This is actually a workaround, since type resolution doesn't resolve the types of lambda parameters. - return true; - } - } - } - return false; - } - - private String getExpectedMessage(final List params, final int expectedArguments) { - return " expected " + expectedArguments + (expectedArguments > 1 ? " arguments " : " argument ") + "but have " - + params.size(); - } - - private boolean isStringFormatCall(ASTExpression node) { - if (node.getNumChildren() > 0 && node.getChild(0) instanceof ASTPrimaryExpression - && node.getChild(0).getNumChildren() > 0 && node.getChild(0).getChild(0) instanceof ASTPrimaryPrefix - && node.getChild(0).getChild(0).getNumChildren() > 0 && node.getChild(0).getChild(0).getChild(0) instanceof ASTName) { - String name = node.getChild(0).getChild(0).getChild(0).getImage(); - - return "String.format".equals(name) || formatIsStringFormat && "format".equals(name); - } - return false; - } - - private int expectedArguments(final ASTExpression node) { - int count = -1; - // look if the logger has a literal message - if (node.getFirstDescendantOfType(ASTLiteral.class) != null) { - count = countPlaceholders(node); - } else if (node.getFirstDescendantOfType(ASTName.class) != null) { - final String variableName = node.getFirstDescendantOfType(ASTName.class).getImage(); - // look if the message is defined locally in a method/constructor, initializer block or lambda expression - final NodeStream parentBlock = - node.ancestors() - .map(asInstanceOf(ASTMethodOrConstructorDeclaration.class, ASTInitializer.class, ASTLambdaExpression.class)) - .take(1) - .descendants(ASTVariableDeclarator.class); - - count = getAmountOfExpectedArguments(variableName, parentBlock); - - if (count == -1) { - // look if the message is defined in a field - // only look for ASTVariableDeclarator that are Fields - final NodeStream fields = - node.ancestors() - .map(asInstanceOf(ASTClassOrInterfaceBody.class, ASTEnumBody.class)) - .take(1) - .descendants(ASTFieldDeclaration.class) - .firstChild(ASTVariableDeclarator.class); - count = getAmountOfExpectedArguments(variableName, fields); - } - } - return count; - } - - private int getAmountOfExpectedArguments(final String variableName, final Iterable variables) { - for (final ASTVariableDeclarator astVariableDeclarator : variables) { - if (astVariableDeclarator.getFirstChildOfType(ASTVariableDeclaratorId.class).getImage() - .equals(variableName)) { - ASTVariableInitializer variableInitializer = astVariableDeclarator - .getFirstDescendantOfType(ASTVariableInitializer.class); - ASTExpression expression = null; - if (variableInitializer != null) { - expression = variableInitializer.getFirstChildOfType(ASTExpression.class); - } - if (expression != null) { - return countPlaceholders(expression); - } - } - } - return -1; - } - - private int countPlaceholders(final ASTExpression node) { - // ignore if String.format - if (isStringFormatCall(node)) { - return -1; - } - - List literals = getStringLiterals(node); - if (literals.isEmpty()) { - // -1 we could not analyze the message parameter - return -1; - } - - // if there are multiple literals, we just assume, they are concatenated - // together... + private static int countPlaceHolders(@NonNull String constValue) { int result = 0; - for (ASTLiteral stringLiteral : literals) { - Matcher matcher = PLACEHOLDER_AND_FORMAT_SPECIFIER.matcher(stringLiteral.getImage()); - while (matcher.find()) { - String format = matcher.group(); - if (!"%%".equals(format) && !"%n".equals(format)) { - result++; - } + Matcher matcher = PLACEHOLDER_AND_FORMAT_SPECIFIER.matcher(constValue); + while (matcher.find()) { + String format = matcher.group(); + if (!"%%".equals(format) && !"%n".equals(format)) { + result++; } } return result; } - private List getStringLiterals(final Node node) { - List stringLiterals = new ArrayList<>(); - for (ASTLiteral literal : node.findDescendantsOfType(ASTLiteral.class)) { - if (literal.isStringLiteral()) { - stringLiterals.add(literal); + private static OptionalInt expectedArguments0(final ASTExpression node) { + if (node.getConstValue() instanceof String) { + return OptionalInt.of(countPlaceHolders((String) node.getConstValue())); + } else if (node instanceof ASTNamedReferenceExpr) { + DataflowResult dataflow = DataflowPass.getDataflowResult(node.getRoot()); + ReachingDefinitionSet reaching = dataflow.getReachingDefinitions((ASTNamedReferenceExpr) node); + if (reaching == null || reaching.isNotFullyKnown()) { + return OptionalInt.empty(); + } + + AssignmentEntry assignment = CollectionUtil.asSingle(reaching.getReaching()); + if (assignment == null) { + return OptionalInt.empty(); + } + + ASTExpression rhs = assignment.getRhsAsExpression(); + if (rhs != null && rhs.getConstValue() instanceof String) { + return OptionalInt.of(countPlaceHolders((String) rhs.getConstValue())); } } - return stringLiterals; + return OptionalInt.empty(); } + + private String getExpectedMessage(final int providedArguments, final int expectedArguments) { + return " expected " + expectedArguments + + (expectedArguments > 1 ? " arguments " : " argument ") + + "but found " + providedArguments; + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java index 7fce5beb3c..437e79bba8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -4,16 +4,13 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.List; - import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; -import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters; import net.sourceforge.pmd.lang.java.ast.ASTImplementsList; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; public class OverrideBothEqualsAndHashcodeRule extends AbstractJavaRule { @@ -46,51 +43,30 @@ public class OverrideBothEqualsAndHashcodeRule extends AbstractJavaRule { @Override public Object visit(ASTImplementsList node, Object data) { - for (int ix = 0; ix < node.getNumChildren(); ix++) { - if (node.getChild(ix) instanceof ASTClassOrInterfaceType) { - ASTClassOrInterfaceType cit = (ASTClassOrInterfaceType) node.getChild(ix); - Class clazz = cit.getType(); - if (clazz != null && node.getChild(ix).hasImageEqualTo("Comparable")) { - implementsComparable = true; - return data; - } - } - } + implementsComparable = node.children().filter(child -> TypeTestUtil.isA(Comparable.class, child)).nonEmpty(); return super.visit(node, data); } @Override - public Object visit(ASTMethodDeclarator node, Object data) { + public Object visit(ASTMethodDeclaration node, Object data) { if (implementsComparable) { return data; } - int iFormalParams = 0; - String paramName = null; - for (int ix = 0; ix < node.getNumChildren(); ix++) { - Node sn = node.getChild(ix); - if (sn instanceof ASTFormalParameters) { - List allParams = ((ASTFormalParameters) sn) - .findChildrenOfType(ASTFormalParameter.class); - for (ASTFormalParameter formalParam : allParams) { - iFormalParams++; - ASTClassOrInterfaceType param = formalParam.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - if (param != null) { - paramName = param.getImage(); - } - } - } + int formalParamsCount = node.getFormalParameters().size(); + ASTFormalParameter formalParam = null; + if (formalParamsCount > 0) { + formalParam = node.getFormalParameters().get(0); } - if (iFormalParams == 0 && node.hasImageEqualTo("hashCode")) { + if (formalParamsCount == 0 && "hashCode".equals(node.getName())) { containsHashCode = true; nodeFound = node; - } else if (iFormalParams == 1 && node.hasImageEqualTo("equals") - && ("Object".equals(paramName) || "java.lang.Object".equals(paramName))) { + } else if (formalParamsCount == 1 && "equals".equals(node.getName()) + && TypeTestUtil.isExactlyA(Object.class, formalParam)) { containsEquals = true; nodeFound = node; } return super.visit(node, data); } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationRule.java index d55a94c278..2d91c57d58 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationRule.java @@ -5,25 +5,26 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; -public class ProperCloneImplementationRule extends AbstractJavaRule { +public class ProperCloneImplementationRule extends AbstractJavaRulechainRule { public ProperCloneImplementationRule() { - addRuleChainVisit(ASTMethodDeclaration.class); + super(ASTMethodDeclaration.class); } @Override public Object visit(ASTMethodDeclaration method, Object data) { if (isCloneMethod(method) && isNotAbstractMethod(method)) { - ASTClassOrInterfaceDeclaration classDecl = method.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); - if (isNotFinal(classDecl) && hasAnyAllocationOfClass(method, classDecl.getSimpleName())) { + ASTAnyTypeDeclaration enclosingType = method.getEnclosingType(); + if (isNotFinal(enclosingType) && hasAnyAllocationOfClass(method, enclosingType)) { addViolation(data, method); } } @@ -38,22 +39,15 @@ public class ProperCloneImplementationRule extends AbstractJavaRule { return !method.isAbstract(); } - private boolean isNotFinal(ASTClassOrInterfaceDeclaration classOrInterfaceDecl) { - return !classOrInterfaceDecl.isFinal(); + private boolean isNotFinal(ASTAnyTypeDeclaration classOrInterfaceDecl) { + return !classOrInterfaceDecl.hasModifiers(JModifier.FINAL); } - private boolean hasAnyAllocationOfClass(ASTMethodDeclaration method, String className) { - List allocations = method.findDescendantsOfType(ASTAllocationExpression.class); - for (ASTAllocationExpression allocation : allocations) { - ASTClassOrInterfaceType allocatedType = allocation.getFirstChildOfType(ASTClassOrInterfaceType.class); - if (isSimpleNameOfType(className, allocatedType)) { - return true; - } - } - return false; - } - - private boolean isSimpleNameOfType(String simpleName, ASTClassOrInterfaceType type) { - return type != null && type.hasImageEqualTo(simpleName); + private boolean hasAnyAllocationOfClass(ASTMethodDeclaration method, ASTAnyTypeDeclaration enclosingType) { + @NonNull + JClassSymbol typeSymbol = enclosingType.getTypeMirror().getSymbol(); + return method.descendants(ASTConstructorCall.class) + .filter(ctor -> ctor.getTypeMirror().getSymbol().equals(typeSymbol)) + .nonEmpty(); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingleMethodSingletonRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingleMethodSingletonRule.java index 028fc78c9e..a67c279550 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingleMethodSingletonRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingleMethodSingletonRule.java @@ -5,47 +5,33 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.List; - import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; /** * Returns Checks if the singleton rule is used properly. */ -public class SingleMethodSingletonRule extends AbstractJavaRule { +public class SingleMethodSingletonRule extends AbstractJavaRulechainRule { + + public SingleMethodSingletonRule() { + super(ASTClassOrInterfaceDeclaration.class); + } /** * Checks for getInstance method usage in the same class. * @param node of ASTCLass * @param data of Object * @return Object - * */ - - @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - - - List methods = node.findDescendantsOfType(ASTMethodDeclaration.class); // Find the name of methods in it - - int count = 0; - for (ASTMethodDeclaration method : methods) { - - if ("getInstance".equals(method.getName())) { - count++; - if (count > 1) { - addViolation(data, node); - break; - } - } - + int count = node.descendants(ASTMethodDeclaration.class) + .filter(m -> "getInstance".equals(m.getName())) + .count(); + if (count > 1) { + addViolation(data, node); } - - - return super.visit(node, data); - + return data; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingletonClassReturningNewInstanceRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingletonClassReturningNewInstanceRule.java index c6f354e92d..0ef09faa5e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingletonClassReturningNewInstanceRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingletonClassReturningNewInstanceRule.java @@ -4,114 +4,39 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.List; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; -import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; +import net.sourceforge.pmd.lang.ast.NodeStream; +import net.sourceforge.pmd.lang.ast.NodeStream.DescendantNodeStream; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; -public class SingletonClassReturningNewInstanceRule extends AbstractJavaRule { +public class SingletonClassReturningNewInstanceRule extends AbstractJavaRulechainRule { + + public SingletonClassReturningNewInstanceRule() { + super(ASTMethodDeclaration.class); + } @Override public Object visit(ASTMethodDeclaration node, Object data) { - - boolean violation = false; - String localVarName = null; - String returnVariableName = null; - - if (node.isVoid()) { - return super.visit(node, data); + if (node.isVoid() || !"getInstance".equals(node.getName())) { + return data; } - if ("getInstance".equals(node.getName())) { - List rsl = node.findDescendantsOfType(ASTReturnStatement.class); - if (rsl.isEmpty()) { - return super.visit(node, data); - } else { - for (ASTReturnStatement rs : rsl) { - - List pel = rs.findDescendantsOfType(ASTPrimaryExpression.class); - ASTPrimaryExpression ape = pel.get(0); - if (ape.getFirstDescendantOfType(ASTAllocationExpression.class) != null) { - violation = true; - break; - } - } - } - - /* - * public class Singleton { - * - * private static Singleton m_instance=null; - * - * public static Singleton getInstance() { - * - * Singleton m_instance=null; - * - * if ( m_instance == null ) { synchronized(Singleton.class) { - * if(m_instance == null) { m_instance = new Singleton(); } } } - * return m_instance; } } - */ - - List astBlockStatements = node.findDescendantsOfType(ASTBlockStatement.class); - returnVariableName = getReturnVariableName(node); - if (!astBlockStatements.isEmpty()) { - for (ASTBlockStatement blockStatement : astBlockStatements) { - if (blockStatement.hasDescendantOfType(ASTLocalVariableDeclaration.class)) { - List lVarList = blockStatement - .findDescendantsOfType(ASTLocalVariableDeclaration.class); - if (!lVarList.isEmpty()) { - for (ASTLocalVariableDeclaration localVar : lVarList) { - for (ASTVariableDeclaratorId id : localVar) { - localVarName = id.getVariableName(); - if (returnVariableName != null && returnVariableName.equals(localVarName)) { - violation = true; - break; - } - } - } - } - } - } - } - } - if (violation) { + DescendantNodeStream rsl = node.descendants(ASTReturnStatement.class); + if (returnsNewInstances(rsl) || returnsLocalVariables(rsl)) { addViolation(data, node); } - return super.visit(node, data); + return data; } - private String getReturnVariableName(ASTMethodDeclaration node) { - - List rsl = node.findDescendantsOfType(ASTReturnStatement.class); - ASTReturnStatement rs = rsl.get(0); - List pel = rs.findDescendantsOfType(ASTPrimaryExpression.class); - ASTPrimaryExpression ape = pel.get(0); - Node lastChild = ape.getChild(0); - String returnVariableName = null; - if (lastChild instanceof ASTPrimaryPrefix) { - returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild); - } - /* - * if(lastChild instanceof ASTPrimarySuffix){ returnVariableName = - * getNameFromPrimarySuffix((ASTPrimarySuffix) lastChild); } - */ - return returnVariableName; - + private boolean returnsNewInstances(NodeStream returns) { + return returns.descendants(ASTConstructorCall.class).nonEmpty(); } - private String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) { - if (pp.getNumChildren() == 1 && pp.getChild(0) instanceof ASTName) { - return ((ASTName) pp.getChild(0)).getImage(); - } - return null; + private boolean returnsLocalVariables(NodeStream returns) { + return returns.children(ASTVariableAccess.class).filter(JavaRuleUtil::isReferenceToLocal).nonEmpty(); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousHashcodeMethodNameRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousHashcodeMethodNameRule.java deleted file mode 100644 index a70311fa2a..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousHashcodeMethodNameRule.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; -import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; -import net.sourceforge.pmd.lang.java.ast.ASTResultType; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class SuspiciousHashcodeMethodNameRule extends AbstractJavaRule { - - @Override - public Object visit(ASTMethodDeclaration node, Object data) { - /* - * original XPath rule was //MethodDeclaration [ResultType - * //PrimitiveType [@Image='int'] [//MethodDeclarator [@Image='hashcode' - * or @Image='HashCode' or @Image='Hashcode'] - * [not(FormalParameters/*)]]] - */ - - ASTResultType type = node.getResultType(); - ASTMethodDeclarator decl = node.getFirstChildOfType(ASTMethodDeclarator.class); - String name = decl.getImage(); - if ("hashcode".equalsIgnoreCase(name) && !"hashCode".equals(name) - && decl.getChild(0).getNumChildren() == 0 && type.getNumChildren() != 0) { - Node t = type.getChild(0).getChild(0); - if (t instanceof ASTPrimitiveType && "int".equals(t.getImage())) { - addViolation(data, node); - return data; - } - } - return super.visit(node, data); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousOctalEscapeRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousOctalEscapeRule.java index 81910bf24c..17edef59b1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousOctalEscapeRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousOctalEscapeRule.java @@ -4,80 +4,82 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -public class SuspiciousOctalEscapeRule extends AbstractJavaRule { +public class SuspiciousOctalEscapeRule extends AbstractJavaRulechainRule { + + public SuspiciousOctalEscapeRule() { + super(ASTStringLiteral.class); + } @Override - public Object visit(ASTLiteral node, Object data) { - if (node.isStringLiteral()) { - String image = node.getImage(); - // trim quotes - String s = image.substring(1, image.length() - 1); + public Object visit(ASTStringLiteral node, Object data) { + String image = node.getImage(); + // trim quotes + String s = image.substring(1, image.length() - 1); - // process escape sequences - int offset = 0; - for (int slash = s.indexOf('\\', offset); slash != -1 - && slash < s.length() - 1; slash = s.indexOf('\\', offset)) { - String escapeSequence = s.substring(slash + 1); - char first = escapeSequence.charAt(0); - offset = slash + 1; // next offset - after slash + // process escape sequences + int offset = 0; + for (int slash = s.indexOf('\\', offset); slash != -1 + && slash < s.length() - 1; slash = s.indexOf('\\', offset)) { + String escapeSequence = s.substring(slash + 1); + char first = escapeSequence.charAt(0); + offset = slash + 1; // next offset - after slash - if (isOctal(first)) { - if (escapeSequence.length() > 1) { - char second = escapeSequence.charAt(1); - if (isOctal(second)) { - if (escapeSequence.length() > 2) { - char third = escapeSequence.charAt(2); - if (isOctal(third)) { - // this is either a three digit octal escape - // or a two-digit - // octal escape followed by an octal digit. - // the value of - // the first digit in the sequence - // determines which is the - // case - if (first != '0' && first != '1' && first != '2' && first != '3') { - // VIOLATION: it's a two-digit octal - // escape followed by - // an octal digit -- legal but very - // confusing! - addViolation(data, node); - } else { - // if there is a 4th decimal digit, it - // could never be part of - // the escape sequence, which is - // confusing - if (escapeSequence.length() > 3) { - char fourth = escapeSequence.charAt(3); - if (isDecimal(fourth)) { - addViolation(data, node); - } + if (isOctal(first)) { + if (escapeSequence.length() > 1) { + char second = escapeSequence.charAt(1); + if (isOctal(second)) { + if (escapeSequence.length() > 2) { + char third = escapeSequence.charAt(2); + if (isOctal(third)) { + // this is either a three digit octal escape + // or a two-digit + // octal escape followed by an octal digit. + // the value of + // the first digit in the sequence + // determines which is the + // case + if (first != '0' && first != '1' && first != '2' && first != '3') { + // VIOLATION: it's a two-digit octal + // escape followed by + // an octal digit -- legal but very + // confusing! + addViolation(data, node, "\\" + first + second + " + " + third); + } else { + // if there is a 4th decimal digit, it + // could never be part of + // the escape sequence, which is + // confusing + if (escapeSequence.length() > 3) { + char fourth = escapeSequence.charAt(3); + if (isDecimal(fourth)) { + addViolation(data, node, "\\" + first + second + third + " + " + fourth); } } - - } else if (isDecimal(third)) { - // this is a two-digit octal escape followed - // by a decimal digit - // legal but very confusing - addViolation(data, node); } + + } else if (isDecimal(third)) { + // this is a two-digit octal escape followed + // by a decimal digit + // legal but very confusing + addViolation(data, node, "\\" + first + second + " + " + third); } - } else if (isDecimal(second)) { - // this is a one-digit octal escape followed by a - // decimal digit - // legal but very confusing - addViolation(data, node); } + } else if (isDecimal(second)) { + // this is a one-digit octal escape followed by a + // decimal digit + // legal but very confusing + addViolation(data, node, "\\" + first + " + " + second); } - } else if (first == '\\') { - offset++; } + } else if (first == '\\') { + offset++; } } - return super.visit(node, data); + return data; } private boolean isOctal(char c) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryCaseChangeRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryCaseChangeRule.java index 62512bedc8..510a0a1142 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryCaseChangeRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryCaseChangeRule.java @@ -8,115 +8,40 @@ import static java.util.Arrays.asList; import java.util.List; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -public class UnnecessaryCaseChangeRule extends AbstractJavaRule { +public class UnnecessaryCaseChangeRule extends AbstractJavaRulechainRule { private static final List CASE_CHANGING_METHODS = asList("toLowerCase", "toUpperCase"); private static final List EQUALITY_METHODS = asList("equals", "equalsIgnoreCase"); + public UnnecessaryCaseChangeRule() { + super(ASTMethodCall.class); + } + @Override - public Object visit(ASTPrimaryExpression expr, Object data) { - if (hasUnnecessaryCaseChange(expr)) { - addViolation(data, expr); - } - return super.visit(expr, data); - } - - private boolean hasUnnecessaryCaseChange(ASTPrimaryExpression expr) { - int equalsMethodCallIndex = getEqualsMethodCallIndex(expr); - if (equalsMethodCallIndex != -1) { - int equalsMethodCallArgsIndex = equalsMethodCallIndex + 1; - ASTPrimaryExpression equalsCallArgs = getMethodCallArgsAtPosition(expr, equalsMethodCallArgsIndex); - return anyHasCaseChangingMethodCall(expr, equalsCallArgs); - } - return false; - } - - private int getEqualsMethodCallIndex(ASTPrimaryExpression expr) { - for (int callIndex = 0; callIndex < expr.getNumChildren(); callIndex++) { - JavaNode methodCall = expr.getChild(callIndex); - if (isEqualsMethodCall(methodCall)) { - return callIndex; + public Object visit(ASTMethodCall node, Object data) { + if (EQUALITY_METHODS.contains(node.getMethodName()) && node.getArguments().size() == 1) { + if (isCaseChangingMethodCall(node.getQualifier()) + || isCaseChangingMethodCall(node.getArguments().get(0))) { + addViolation(data, node); } } - return -1; + return data; } - private boolean isEqualsMethodCall(JavaNode methodCall) { - return calledMethodHasNameFromList(methodCall, EQUALITY_METHODS); - } - - private ASTPrimaryExpression getMethodCallArgsAtPosition(ASTPrimaryExpression expr, int argsPos) { - if (hasChildAtPosition(expr, argsPos)) { - JavaNode methodCallArgs = expr.getChild(argsPos); - return methodCallArgs.getFirstDescendantOfType(ASTPrimaryExpression.class); - } - return null; - } - - private boolean hasChildAtPosition(ASTPrimaryExpression expr, int pos) { - return expr.getNumChildren() > pos; - } - - private boolean anyHasCaseChangingMethodCall(ASTPrimaryExpression ... exprs) { - for (ASTPrimaryExpression expr : exprs) { - if (expr != null && hasCaseChangingMethodCall(expr)) { - return true; - } + /** + * Checks for toLower/UpperCase method calls without arguments. + * These method take an optional Locale as an argument - in that case, + * these case conversions are considered deliberate. + */ + private boolean isCaseChangingMethodCall(ASTExpression expr) { + if (expr instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) expr; + return CASE_CHANGING_METHODS.contains(call.getMethodName()) && call.getArguments().size() == 0; } return false; } - - private boolean hasCaseChangingMethodCall(ASTPrimaryExpression expr) { - for (int callArgsIndex = 1; callArgsIndex < expr.getNumChildren(); callArgsIndex++) { - JavaNode methodCall = expr.getChild(callArgsIndex - 1); - JavaNode methodCallArgs = expr.getChild(callArgsIndex); - if (isCaseChangingMethodCall(methodCall, methodCallArgs)) { - return true; - } - } - return false; - } - - private boolean isCaseChangingMethodCall(JavaNode methodCall, JavaNode methodCallArgs) { - if (calledMethodHasNameFromList(methodCall, CASE_CHANGING_METHODS)) { - ASTArguments args = methodCallArgs.getFirstDescendantOfType(ASTArguments.class); - return args != null && args.size() == 0; - } - return false; - } - - private boolean calledMethodHasNameFromList(JavaNode methodCall, List nameList) { - String methodName = getCalledMethodName(methodCall); - if (methodName != null) { - for (String nameFromList : nameList) { - if (methodName.endsWith(nameFromList)) { - return true; - } - } - } - return false; - } - - private String getCalledMethodName(JavaNode methodCall) { - String methodName = methodCall.getImage(); - if (methodName == null) { - ASTName name = methodCall.getFirstDescendantOfType(ASTName.class); - return name != null ? methodNameFromCallImage(name.getImage()) : null; - } - return methodName; - } - - private String methodNameFromCallImage(String methodCallImage) { - if (methodCallImage.contains(".")) { - String[] callParts = methodCallImage.split("\\."); - return callParts[1]; - } - return methodCallImage; - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryConversionTemporaryRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryConversionTemporaryRule.java deleted file mode 100644 index 8b5d74d6d6..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryConversionTemporaryRule.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import static net.sourceforge.pmd.util.CollectionUtil.setOf; - -import java.util.Set; - -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class UnnecessaryConversionTemporaryRule extends AbstractJavaRule { - - private boolean inPrimaryExpressionContext; - private ASTPrimaryExpression primary; - private boolean usingPrimitiveWrapperAllocation; - - private static final Set PRIMITIVE_WRAPPERS = setOf("Integer", "Boolean", "Double", "Long", "Short", "Byte", "Float"); - - @Override - public Object visit(ASTPrimaryExpression node, Object data) { - if (node.getNumChildren() == 0 || node.getChild(0).getNumChildren() == 0 - || !(node.getChild(0).getChild(0) instanceof ASTAllocationExpression)) { - return super.visit(node, data); - } - // TODO... hmmm... is this inPrimaryExpressionContext gibberish - // necessary? - inPrimaryExpressionContext = true; - primary = node; - super.visit(node, data); - inPrimaryExpressionContext = false; - usingPrimitiveWrapperAllocation = false; - return data; - } - - @Override - public Object visit(ASTAllocationExpression node, Object data) { - if (!inPrimaryExpressionContext || !(node.getChild(0) instanceof ASTClassOrInterfaceType)) { - return super.visit(node, data); - } - if (!PRIMITIVE_WRAPPERS.contains(node.getChild(0).getImage())) { - return super.visit(node, data); - } - usingPrimitiveWrapperAllocation = true; - return super.visit(node, data); - } - - @Override - public Object visit(ASTPrimarySuffix node, Object data) { - if (inPrimaryExpressionContext && usingPrimitiveWrapperAllocation) { - if (node.hasImageEqualTo("toString")) { - if (node.getParent() == primary) { - addViolation(data, node); - } - } - } - return super.visit(node, data); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java index 9e11cb9b70..87955a509d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java @@ -22,7 +22,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTMemberValue; import net.sourceforge.pmd.lang.java.ast.ASTMemberValuePair; import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.Annotatable; -import net.sourceforge.pmd.lang.java.rule.errorprone.MissingBreakInSwitchRule; +import net.sourceforge.pmd.lang.java.rule.errorprone.ImplicitSwitchFallThroughRule; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** @@ -120,7 +120,7 @@ final class AnnotationSuppressionUtil { || "all".equals(stringVal) || "serial".equals(stringVal) && SERIAL_RULES.contains(rule.getName()) || "unused".equals(stringVal) && UNUSED_RULES.contains(rule.getName()) - || "fallthrough".equals(stringVal) && rule instanceof MissingBreakInSwitchRule + || "fallthrough".equals(stringVal) && rule instanceof ImplicitSwitchFallThroughRule ) { return true; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/DataflowPass.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/DataflowPass.java index 4ed0cc44b0..68f38e1f21 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/DataflowPass.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/DataflowPass.java @@ -171,7 +171,7 @@ public final class DataflowPass { private final boolean isNotFullyKnown; private final boolean containsInitialFieldValue; - ReachingDefinitionSet(Set reaching) { + ReachingDefinitionSet(/*Mutable*/Set reaching) { this.reaching = reaching; this.containsInitialFieldValue = reaching.removeIf(AssignmentEntry::isFieldAssignmentAtStartOfMethod); // not || as we want the side effect @@ -245,7 +245,47 @@ public final class DataflowPass { public @Nullable ReachingDefinitionSet getReachingDefinitions(ASTNamedReferenceExpr expr) { - return expr.getUserMap().get(REACHING_DEFS); + return expr.getUserMap().computeIfAbsent(REACHING_DEFS, () -> reachingFallback(expr)); + } + + // Fallback, to compute reaching definitions for some fields + // that are not tracked by the tree exploration. Final fields + // indeed have a fully known set of reaching definitions. + // TODO maybe they should actually be tracked? + private @Nullable ReachingDefinitionSet reachingFallback(ASTNamedReferenceExpr expr) { + JVariableSymbol sym = expr.getReferencedSym(); + if (sym == null || !sym.isField() || !sym.isFinal()) { + return null; + } + + ASTVariableDeclaratorId node = sym.tryGetNode(); + if (node == null) { + return null; // we don't care about non-local declarations + } + Set assignments = node.getLocalUsages() + .stream() + .filter(it -> it.getAccessType() == AccessType.WRITE) + .map(usage -> { + JavaNode parent = usage.getParent(); + if (parent instanceof ASTUnaryExpression + && !((ASTUnaryExpression) parent).getOperator().isPure()) { + return parent; + } else if (usage.getIndexInParent() == 0 + && parent instanceof ASTAssignmentExpression) { + return ((ASTAssignmentExpression) parent).getRightOperand(); + } else { + return null; + } + }).filter(Objects::nonNull) + .map(it -> new AssignmentEntry(sym, node, it)) + .collect(CollectionUtil.toMutableSet()); + + ASTExpression init = node.getInitializer(); // this one is not in the usages + if (init != null) { + assignments.add(new AssignmentEntry(sym, node, init)); + } + + return new ReachingDefinitionSet(assignments); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java index 4d802eba02..5aa285da68 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java @@ -16,7 +16,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -31,6 +30,7 @@ import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTArrayAccess; +import net.sourceforge.pmd.lang.java.ast.ASTArrayAllocation; import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr; import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.AccessType; @@ -42,6 +42,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTForStatement; @@ -76,12 +77,14 @@ import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.ast.UnaryOp; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; +import net.sourceforge.pmd.lang.java.symbols.internal.ast.AstLocalVarSym; import net.sourceforge.pmd.lang.java.types.InvocationMatcher; import net.sourceforge.pmd.lang.java.types.InvocationMatcher.CompoundInvocationMatcher; import net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind; import net.sourceforge.pmd.lang.java.types.JTypeMirror; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; import net.sourceforge.pmd.util.CollectionUtil; +import net.sourceforge.pmd.util.OptionalBool; /** * Utilities shared between rules. @@ -465,9 +468,9 @@ public final class JavaRuleUtil { private static boolean isReadUsage(ASTNamedReferenceExpr expr) { return expr.getAccessType() == AccessType.READ - // foo(x++) + // x++ as a method argument or used in other expression || expr.getParent() instanceof ASTUnaryExpression - && expr.getParent().getParent() instanceof ASTArgumentList; + && !(expr.getParent().getParent() instanceof ASTExpressionStatement); } /** @@ -658,7 +661,7 @@ public final class JavaRuleUtil { return e instanceof ASTBooleanLiteral; } - public static boolean isBooleanNegation(ASTExpression e) { + public static boolean isBooleanNegation(JavaNode e) { return e instanceof ASTUnaryExpression && ((ASTUnaryExpression) e).getOperator() == UnaryOp.NEGATION; } @@ -770,8 +773,7 @@ public final class JavaRuleUtil { */ public static boolean isReferenceToSameVar(ASTExpression e1, ASTExpression e2) { if (e1 instanceof ASTNamedReferenceExpr && e2 instanceof ASTNamedReferenceExpr) { - if (!Objects.equals(((ASTNamedReferenceExpr) e2).getReferencedSym(), - ((ASTNamedReferenceExpr) e1).getReferencedSym())) { + if (OptionalBool.YES != referenceSameSymbol((ASTNamedReferenceExpr) e1, (ASTNamedReferenceExpr) e2)) { return false; } @@ -790,13 +792,24 @@ public final class JavaRuleUtil { return false; } + private static OptionalBool referenceSameSymbol(ASTNamedReferenceExpr e1, ASTNamedReferenceExpr e2) { + if (!e1.getName().equals(e2.getName())) { + return OptionalBool.NO; + } + JVariableSymbol ref1 = e1.getReferencedSym(); + JVariableSymbol ref2 = e2.getReferencedSym(); + if (ref1 == null || ref2 == null) { + return OptionalBool.UNKNOWN; + } + return OptionalBool.definitely(ref1.equals(ref2)); + } + /** * Returns true if the expression is a reference to a local variable. */ public static boolean isReferenceToLocal(ASTExpression expr) { if (expr instanceof ASTVariableAccess) { - JVariableSymbol sym = ((ASTVariableAccess) expr).getReferencedSym(); - return sym != null && !sym.isField(); + return ((ASTVariableAccess) expr).getReferencedSym() instanceof AstLocalVarSym; } return false; } @@ -952,4 +965,31 @@ public final class JavaRuleUtil { public static boolean hasLombokAnnotation(Annotatable node) { return LOMBOK_ANNOTATIONS.stream().anyMatch(node::isAnnotationPresent); } + + /** + * Returns true if the expression is a null check on the given variable. + */ + public static boolean isNullCheck(ASTExpression expr, JVariableSymbol var) { + return isNullCheck(expr, StablePathMatcher.matching(var)); + } + + public static boolean isNullCheck(ASTExpression expr, StablePathMatcher matcher) { + if (expr instanceof ASTInfixExpression) { + ASTInfixExpression condition = (ASTInfixExpression) expr; + if (condition.getOperator().hasSamePrecedenceAs(BinaryOp.EQ)) { + ASTNullLiteral nullLit = condition.firstChild(ASTNullLiteral.class); + if (nullLit != null) { + return matcher.matches(getOtherOperandIfInInfixExpr(nullLit)); + } + } + } + return false; + } + + public static boolean isArrayInitializer(ASTExpression expr) { + if (expr instanceof ASTArrayAllocation) { + return ((ASTArrayAllocation) expr).getArrayInitializer() != null; + } + return false; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/StablePathMatcher.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/StablePathMatcher.java index f3c43de9f7..c6df092758 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/StablePathMatcher.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/StablePathMatcher.java @@ -4,7 +4,9 @@ package net.sourceforge.pmd.lang.java.rule.internal; -import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; import org.checkerframework.checker.nullness.qual.Nullable; @@ -31,9 +33,9 @@ public final class StablePathMatcher { // if owner == null, then the owner is `this`. private final @Nullable JVariableSymbol owner; - private final ArrayDeque path; + private final List path; - private StablePathMatcher(@Nullable JVariableSymbol owner, ArrayDeque path) { + private StablePathMatcher(@Nullable JVariableSymbol owner, List path) { this.owner = owner; this.path = path; } @@ -88,18 +90,21 @@ public final class StablePathMatcher { * Otherwise returns null. */ public static @Nullable StablePathMatcher matching(ASTExpression e) { + if (e == null) { + return null; + } JVariableSymbol owner = null; - ArrayDeque segments = new ArrayDeque<>(); + List segments = new ArrayList<>(); while (e != null) { if (e instanceof ASTFieldAccess) { ASTFieldAccess access = (ASTFieldAccess) e; - segments.addLast(new Segment(access.getName(), true)); + segments.add(new Segment(access.getName(), true)); e = access.getQualifier(); } else if (e instanceof ASTMethodCall) { ASTMethodCall call = (ASTMethodCall) e; if (JavaRuleUtil.isGetterCall(call)) { - segments.addLast(new Segment(call.getMethodName(), false)); + segments.add(new Segment(call.getMethodName(), false)); e = call.getQualifier(); } else { return null; @@ -128,6 +133,10 @@ public final class StablePathMatcher { return new StablePathMatcher(owner, segments); } + public static StablePathMatcher matching(JVariableSymbol e) { + return new StablePathMatcher(e, Collections.emptyList()); + } + private static final class Segment { final String name; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java index 04427bf008..a944309044 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java @@ -14,15 +14,13 @@ import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; -import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.ast.BinaryOp; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; @@ -93,15 +91,15 @@ public class DoubleCheckedLockingRule extends AbstractJavaRule { List isl = node.findDescendantsOfType(ASTIfStatement.class); if (isl.size() == 2) { ASTIfStatement outerIf = isl.get(0); - if (isNullCheck(outerIf.getCondition(), returnVariable)) { + if (JavaRuleUtil.isNullCheck(outerIf.getCondition(), returnVariable)) { // find synchronized List ssl = outerIf.findDescendantsOfType(ASTSynchronizedStatement.class); if (ssl.size() == 1 && ssl.get(0).ancestors().any(it -> it == outerIf)) { ASTIfStatement is2 = isl.get(1); - if (isNullCheck(is2.getCondition(), returnVariable)) { + if (JavaRuleUtil.isNullCheck(is2.getCondition(), returnVariable)) { List assignments = is2.findDescendantsOfType(ASTAssignmentExpression.class); if (assignments.size() == 1 - && isReferenceTo(assignments.get(0).getLeftOperand(), returnVariable)) { + && JavaRuleUtil.isReferenceToVar(assignments.get(0).getLeftOperand(), returnVariable)) { addViolation(data, node); } @@ -127,7 +125,7 @@ public class DoubleCheckedLockingRule extends AbstractJavaRule { return (initializer == null || isVolatileFieldReference(initializer)) && method.descendants(ASTAssignmentExpression.class) - .filter(it -> isReferenceTo(it.getLeftOperand(), local)) + .filter(it -> JavaRuleUtil.isReferenceToVar(it.getLeftOperand(), local)) .all(it -> isVolatileFieldReference(it.getRightOperand())); } @@ -140,26 +138,4 @@ public class DoubleCheckedLockingRule extends AbstractJavaRule { } } - private boolean isReferenceTo(@Nullable ASTExpression expr, JVariableSymbol symbol) { - if (expr instanceof ASTNamedReferenceExpr) { - return symbol != null && symbol.equals(((ASTNamedReferenceExpr) expr).getReferencedSym()); - } else { - return false; - } - } - - private boolean isNullCheck(ASTExpression expr, JVariableSymbol var) { - if (expr instanceof ASTInfixExpression) { - ASTInfixExpression condition = (ASTInfixExpression) expr; - if (condition.getOperator().hasSamePrecedenceAs(BinaryOp.EQ)) { - ASTNullLiteral nullLit = condition.getFirstChildOfType(ASTNullLiteral.class); - if (nullLit != null) { - ASTExpression otherChild = (ASTExpression) condition.getChild(1 - nullLit.getIndexInParent()); - return isReferenceTo(otherChild, var); - } - } - } - return false; - } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/NonThreadSafeSingletonRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/NonThreadSafeSingletonRule.java index 6bc0e3a71e..7bbee2ae2e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/NonThreadSafeSingletonRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/NonThreadSafeSingletonRule.java @@ -6,114 +6,107 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import java.util.Map; +import java.util.Set; -import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr; +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; +import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; import net.sourceforge.pmd.properties.PropertyDescriptor; +public class NonThreadSafeSingletonRule extends AbstractJavaRulechainRule { -public class NonThreadSafeSingletonRule extends AbstractJavaRule { - private Map fieldDecls = new HashMap<>(); + private static final PropertyDescriptor CHECK_NON_STATIC_METHODS_DESCRIPTOR = booleanProperty( + "checkNonStaticMethods") + .desc("Check for non-static methods. Do not set this to false and checkNonStaticFields to true.") + .defaultValue(true).build(); + private static final PropertyDescriptor CHECK_NON_STATIC_FIELDS_DESCRIPTOR = booleanProperty( + "checkNonStaticFields") + .desc("Check for non-static fields. Do not set this to true and checkNonStaticMethods to false.") + .defaultValue(false).build(); + + private Set fields = new HashSet<>(); private boolean checkNonStaticMethods = true; private boolean checkNonStaticFields = true; - private static final PropertyDescriptor CHECK_NON_STATIC_METHODS_DESCRIPTOR = - booleanProperty("checkNonStaticMethods") - .desc("Check for non-static methods. Do not set this to false and checkNonStaticFields to true.") - .defaultValue(true).build(); - private static final PropertyDescriptor CHECK_NON_STATIC_FIELDS_DESCRIPTOR = - booleanProperty("checkNonStaticFields") - .desc("Check for non-static fields. Do not set this to true and checkNonStaticMethods to false.") - .defaultValue(false).build(); - - public NonThreadSafeSingletonRule() { + super(ASTFieldDeclaration.class, ASTMethodDeclaration.class); definePropertyDescriptor(CHECK_NON_STATIC_METHODS_DESCRIPTOR); definePropertyDescriptor(CHECK_NON_STATIC_FIELDS_DESCRIPTOR); } + @Override - public Object visit(ASTCompilationUnit node, Object data) { - fieldDecls.clear(); + public void start(RuleContext ctx) { + fields.clear(); checkNonStaticMethods = getProperty(CHECK_NON_STATIC_METHODS_DESCRIPTOR); checkNonStaticFields = getProperty(CHECK_NON_STATIC_FIELDS_DESCRIPTOR); - return super.visit(node, data); } + @Override public Object visit(ASTFieldDeclaration node, Object data) { - if (checkNonStaticFields || node.isStatic()) { - fieldDecls.put(node.getVariableName(), node); + if (checkNonStaticFields || node.hasModifiers(JModifier.STATIC)) { + for (ASTVariableDeclaratorId varId : node.getVarIds()) { + fields.add(varId.getName()); + } } - return super.visit(node, data); + return data; } + @Override public Object visit(ASTMethodDeclaration node, Object data) { - - if (checkNonStaticMethods && !node.isStatic() || node.isSynchronized()) { - return super.visit(node, data); + if (checkNonStaticMethods && !node.hasModifiers(JModifier.STATIC) + || node.hasModifiers(JModifier.SYNCHRONIZED)) { + return data; } - List ifStatements = node.findDescendantsOfType(ASTIfStatement.class); + List ifStatements = node.descendants(ASTIfStatement.class).toList(); for (ASTIfStatement ifStatement : ifStatements) { - if (ifStatement.getFirstParentOfType(ASTSynchronizedStatement.class) == null) { - if (!ifStatement.hasDescendantOfType(ASTNullLiteral.class)) { + if (ifStatement.getCondition().descendants(ASTNullLiteral.class).isEmpty()) { + continue; + } + ASTNamedReferenceExpr n = ifStatement.getCondition().descendants(ASTNamedReferenceExpr.class).first(); + if (n == null || !fields.contains(n.getName())) { + continue; + } + List assignments = ifStatement.descendants(ASTAssignmentExpression.class).toList(); + boolean violation = false; + for (ASTAssignmentExpression assignment : assignments) { + if (assignment.ancestors(ASTSynchronizedStatement.class).nonEmpty()) { continue; } - ASTName n = ifStatement.getFirstDescendantOfType(ASTName.class); - if (n == null || !fieldDecls.containsKey(n.getImage())) { - continue; - } - List assignments = ifStatement - .findDescendantsOfType(ASTAssignmentOperator.class); - boolean violation = false; - for (int ix = 0; ix < assignments.size(); ix++) { - ASTAssignmentOperator oper = assignments.get(ix); - if (!(oper.getParent() instanceof ASTStatementExpression)) { - continue; - } - ASTStatementExpression expr = (ASTStatementExpression) oper.getParent(); - if (expr.getChild(0) instanceof ASTPrimaryExpression - && ((ASTPrimaryExpression) expr.getChild(0)).getNumChildren() == 1 - && ((ASTPrimaryExpression) expr.getChild(0)) - .getChild(0) instanceof ASTPrimaryPrefix) { - ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ((ASTPrimaryExpression) expr.getChild(0)) - .getChild(0); - String name = null; - if (pp.usesThisModifier()) { - ASTPrimarySuffix priSuf = expr.getFirstDescendantOfType(ASTPrimarySuffix.class); - name = priSuf.getImage(); - } else { - ASTName astName = (ASTName) pp.getChild(0); - name = astName.getImage(); - } - if (fieldDecls.containsKey(name)) { + + ASTAssignableExpr left = assignment.getLeftOperand(); + if (left instanceof ASTNamedReferenceExpr) { + JVariableSymbol referencedSym = ((ASTNamedReferenceExpr) left).getReferencedSym(); + if (referencedSym instanceof JFieldSymbol) { + String name = ((ASTNamedReferenceExpr) left).getName(); + if (fields.contains(name)) { violation = true; } } } - if (violation) { - addViolation(data, ifStatement); - } + } + if (violation) { + addViolation(data, ifStatement); } } - return super.visit(node, data); + return data; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/UnsynchronizedStaticFormatterRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/UnsynchronizedStaticFormatterRule.java index 414d547b90..50e2d95ec1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/UnsynchronizedStaticFormatterRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/UnsynchronizedStaticFormatterRule.java @@ -9,16 +9,18 @@ import java.util.Arrays; import java.util.List; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -31,8 +33,7 @@ import net.sourceforge.pmd.properties.PropertyFactory; * @author Allan Caplan * @see feature #226 Check for SimpleDateFormat as singleton? */ -public class UnsynchronizedStaticFormatterRule extends AbstractJavaRule { - private Class formatterClassToCheck = Format.class; +public class UnsynchronizedStaticFormatterRule extends AbstractJavaRulechainRule { private static final List THREAD_SAFE_FORMATTER = Arrays.asList( "org.apache.commons.lang3.time.FastDateFormat" ); @@ -44,8 +45,10 @@ public class UnsynchronizedStaticFormatterRule extends AbstractJavaRule { .defaultValue(false) .build(); + private Class formatterClassToCheck = Format.class; + public UnsynchronizedStaticFormatterRule() { - addRuleChainVisit(ASTFieldDeclaration.class); + super(ASTFieldDeclaration.class); definePropertyDescriptor(ALLOW_METHOD_LEVEL_SYNC); } @@ -56,43 +59,45 @@ public class UnsynchronizedStaticFormatterRule extends AbstractJavaRule { @Override public Object visit(ASTFieldDeclaration node, Object data) { - if (!node.isStatic()) { + if (!node.hasModifiers(JModifier.STATIC)) { return data; } - ASTClassOrInterfaceType cit = node.getFirstDescendantOfType(ASTClassOrInterfaceType.class); + ASTClassOrInterfaceType cit = node.descendants(ASTClassOrInterfaceType.class).first(); if (cit == null || !TypeTestUtil.isA(formatterClassToCheck, cit)) { return data; } - ASTVariableDeclaratorId var = node.getFirstDescendantOfType(ASTVariableDeclaratorId.class); + ASTVariableDeclaratorId var = node.descendants(ASTVariableDeclaratorId.class).first(); for (String formatter: THREAD_SAFE_FORMATTER) { if (TypeTestUtil.isA(formatter, var)) { return data; } } - for (NameOccurrence occ : var.getUsages()) { - Node n = occ.getLocation(); + for (ASTNamedReferenceExpr ref : var.getLocalUsages()) { + ASTMethodCall methodCall = null; + if (ref.getParent() instanceof ASTMethodCall) { + methodCall = (ASTMethodCall) ref.getParent(); + } // ignore usages, that don't call a method. - if (!n.getImage().contains(".")) { + if (methodCall == null) { continue; } + Node n = ref; + // is there a block-level synch? - ASTSynchronizedStatement syncStatement = n.getFirstParentOfType(ASTSynchronizedStatement.class); + ASTSynchronizedStatement syncStatement = ref.ancestors(ASTSynchronizedStatement.class).first(); if (syncStatement != null) { - ASTExpression expression = syncStatement.getFirstChildOfType(ASTExpression.class); - if (expression != null) { - ASTName name = expression.getFirstDescendantOfType(ASTName.class); - if (name != null && name.hasImageEqualTo(var.getName())) { - continue; - } + ASTExpression lockExpression = syncStatement.getLockExpression(); + if (JavaRuleUtil.isReferenceToSameVar(lockExpression, methodCall.getQualifier())) { + continue; } } // method level synch enabled and used? if (getProperty(ALLOW_METHOD_LEVEL_SYNC)) { - ASTMethodDeclaration method = n.getFirstParentOfType(ASTMethodDeclaration.class); - if (method != null && method.isSynchronized() && method.isStatic()) { + ASTMethodDeclaration method = ref.ancestors(ASTMethodDeclaration.class).first(); + if (method != null && method.hasModifiers(JModifier.SYNCHRONIZED, JModifier.STATIC)) { continue; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsRule.java index 7c7fbb7d54..a491bb20f2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsRule.java @@ -7,97 +7,83 @@ package net.sourceforge.pmd.lang.java.rule.performance; import java.util.Collection; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; +import net.sourceforge.pmd.lang.java.ast.ASTArrayAccess; +import net.sourceforge.pmd.lang.java.ast.ASTArrayAllocation; +import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression; import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTBreakStatement; -import net.sourceforge.pmd.lang.java.ast.ASTDoStatement; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTForInit; -import net.sourceforge.pmd.lang.java.ast.ASTForStatement; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; +import net.sourceforge.pmd.lang.java.ast.ASTForeachStatement; +import net.sourceforge.pmd.lang.java.ast.ASTLoopStatement; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; import net.sourceforge.pmd.lang.java.ast.ASTStatement; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; -import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement; import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.ast.TypeNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -public class AvoidInstantiatingObjectsInLoopsRule extends AbstractJavaRule { +public class AvoidInstantiatingObjectsInLoopsRule extends AbstractJavaRulechainRule { public AvoidInstantiatingObjectsInLoopsRule() { - addRuleChainVisit(ASTAllocationExpression.class); + super(ASTConstructorCall.class, ASTArrayAllocation.class); } - /** - * This method is used to check whether user instantiates variables - * which are not assigned to arrays/lists in loops. - * @param node This is the expression of part of java code to be checked. - * @param data This is the data to return. - * @return Object This returns the data passed in. If violation happens, violation is added to data. - */ @Override - public Object visit(ASTAllocationExpression node, Object data) { - if (notInsideLoop(node)) { - return data; - } - - if (fourthParentNotThrow(node) - && fourthParentNotReturn(node) - && notArrayAssignment(node) - && notCollectionAccess(node) - && notBreakFollowing(node)) { - addViolation(data, node); - } + public Object visit(ASTConstructorCall node, Object data) { + checkNode(node, data); return data; } - private boolean notArrayAssignment(ASTAllocationExpression node) { - if (node.getNthParent(4) instanceof ASTStatementExpression) { - ASTPrimaryExpression assignee = node.getNthParent(4).getFirstChildOfType(ASTPrimaryExpression.class); - ASTPrimarySuffix suffix = assignee.getFirstChildOfType(ASTPrimarySuffix.class); - return suffix == null || !suffix.isArrayDereference(); + @Override + public Object visit(ASTArrayAllocation node, Object data) { + checkNode(node, data); + return data; + } + + private void checkNode(JavaNode node, Object data) { + if (notInsideLoop(node)) { + return; + } + + if (notAThrowStatement(node) + && notAReturnStatement(node) + && notBreakFollowing(node) + && notArrayAssignment(node) + && notCollectionAccess(node)) { + addViolation(data, node); + } + } + + private boolean notArrayAssignment(JavaNode node) { + JavaNode childOfAssignment = node.ancestorsOrSelf() + .filter(n -> n.getParent() instanceof ASTAssignmentExpression).first(); + + if (childOfAssignment != null && childOfAssignment.getIndexInParent() == 1) { + Node assignee = childOfAssignment.getParent().getFirstChild(); + return !(assignee instanceof ASTArrayAccess); } return true; } - private boolean notCollectionAccess(ASTAllocationExpression node) { - if (node.getNthParent(4) instanceof ASTArgumentList && node.getNthParent(8) instanceof ASTStatementExpression) { - ASTStatementExpression statement = (ASTStatementExpression) node.getNthParent(8); - return !isCallOnReceiverOfType(Collection.class, statement); - } - return true; + private boolean notCollectionAccess(JavaNode node) { + // checks whether the given ConstructorCall/ArrayAllocation is + // part of a MethodCall on a Collection. + return node.ancestors(ASTArgumentList.class) + .filter(n -> n.getParent() instanceof ASTMethodCall) + .filter(n -> TypeTestUtil.isA(Collection.class, ((ASTMethodCall) n.getParent()).getQualifier())) + .isEmpty(); } - private static boolean isCallOnReceiverOfType(Class receiverType, JavaNode expression) { - if ((expression instanceof ASTExpression || expression instanceof ASTStatementExpression) - && expression.getNumChildren() == 1) { - expression = expression.getChild(0); - } - int numChildren = expression.getNumChildren(); - if (expression instanceof ASTPrimaryExpression && numChildren >= 2) { - JavaNode lastChild = expression.getChild(numChildren - 1); - if (lastChild instanceof ASTPrimarySuffix && ((ASTPrimarySuffix) lastChild).isArguments()) { - JavaNode receiverExpr = expression.getChild(numChildren - 2); - return receiverExpr instanceof TypeNode && TypeTestUtil.isA(receiverType, (TypeNode) receiverExpr); - } - } - return false; - } - - private boolean notBreakFollowing(ASTAllocationExpression node) { - ASTStatement statement = node.getFirstParentOfType(ASTStatement.class); + private boolean notBreakFollowing(JavaNode node) { + JavaNode statement = node.ancestors().filter(n -> n.getParent() instanceof ASTBlock).first(); if (statement != null) { - ASTBlock block = statement.getFirstParentOfType(ASTBlock.class); + ASTBlock block = (ASTBlock) statement.getParent(); if (block.getNumChildren() > statement.getIndexInParent() + 1) { ASTStatement next = block.getChild(statement.getIndexInParent() + 1); - if (next.getNumChildren() == 1 && next.getChild(0).getNumChildren() == 1) { - return !(next.getChild(0).getChild(0) instanceof ASTBreakStatement); - } + return !(next instanceof ASTBreakStatement); } } return true; @@ -106,19 +92,19 @@ public class AvoidInstantiatingObjectsInLoopsRule extends AbstractJavaRule { /** * This method is used to check whether this expression is a throw statement. * @param node This is the expression of part of java code to be checked. - * @return boolean This returns Whether the fourth parent of node is an instance of throw statement. + * @return boolean This returns whether the given constructor call is part of a throw statement */ - private boolean fourthParentNotThrow(ASTAllocationExpression node) { - return !(node.getNthParent(4) instanceof ASTThrowStatement); + private boolean notAThrowStatement(JavaNode node) { + return !(node.getParent() instanceof ASTThrowStatement); } /** * This method is used to check whether this expression is a return statement. * @param node This is the expression of part of java code to be checked. - * @return boolean This returns Whether the fourth parent of node is an instance of return statement. + * @return boolean This returns whether the given constructor call is part of a return statement */ - private boolean fourthParentNotReturn(ASTAllocationExpression node) { - return !(node.getNthParent(4) instanceof ASTReturnStatement); + private boolean notAReturnStatement(JavaNode node) { + return !(node.getParent() instanceof ASTReturnStatement); } /** @@ -126,10 +112,10 @@ public class AvoidInstantiatingObjectsInLoopsRule extends AbstractJavaRule { * @param node This is the expression of part of java code to be checked. * @return boolean false if the given node is inside a loop, true otherwise */ - private boolean notInsideLoop(ASTAllocationExpression node) { - Node n = node.getParent(); + private boolean notInsideLoop(Node node) { + Node n = node; while (n != null) { - if (n instanceof ASTDoStatement || n instanceof ASTWhileStatement || n instanceof ASTForStatement) { + if (n instanceof ASTLoopStatement) { return false; } else if (n instanceof ASTForInit) { /* @@ -137,10 +123,9 @@ public class AvoidInstantiatingObjectsInLoopsRule extends AbstractJavaRule { * ASTForStatement but continue higher up to detect nested loops */ n = n.getParent(); - } else if (n.getParent() instanceof ASTForStatement && n.getParent().getNumChildren() > 1 + } else if (n.getParent() instanceof ASTForeachStatement && n.getParent().getNumChildren() > 1 && n == n.getParent().getChild(1)) { - // it is the second child of a ForStatement - which means - // we are dealing with a for-each construct + // it is the second child of a ForeachStatement. // In that case, we can ignore this allocation expression, as // the second child // is the expression, over which to iterate. diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/BigIntegerInstantiationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/BigIntegerInstantiationRule.java index d367edb06c..27bf187d76 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/BigIntegerInstantiationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/BigIntegerInstantiationRule.java @@ -6,57 +6,55 @@ package net.sourceforge.pmd.lang.java.rule.performance; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Set; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTArrayDimsAndInits; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.util.CollectionUtil; /** - * Rule that marks instantiations of new {@link BigInteger} or - * {@link BigDecimal} objects, when there is a well-known constant available, - * such as {@link BigInteger#ZERO}. + * Rule that marks instantiations of new {@link BigInteger} or {@link BigDecimal} objects, when there is a well-known + * constant available, such as {@link BigInteger#ZERO}. */ -public class BigIntegerInstantiationRule extends AbstractJavaRule { +public class BigIntegerInstantiationRule extends AbstractJavaRulechainRule { + + + private static final Set CONSTANTS = CollectionUtil.setOf("0", "0.", "1"); + + public BigIntegerInstantiationRule() { + super(ASTConstructorCall.class); + } @Override - public Object visit(ASTAllocationExpression node, Object data) { - Node type = node.getChild(0); + public Object visit(ASTConstructorCall node, Object data) { + LanguageVersion languageVersion = node.getTextDocument().getLanguageVersion(); + boolean jdk15 = languageVersion.compareToVersion("1.5") >= 0; + boolean jdk9 = languageVersion.compareToVersion("9") >= 0; - if (!(type instanceof ASTClassOrInterfaceType)) { - return super.visit(node, data); - } + if (TypeTestUtil.isA(BigInteger.class, node) || jdk15 && TypeTestUtil.isA(BigDecimal.class, node)) { - boolean jdk15 = node.getTextDocument().getLanguageVersion().compareTo(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.5")) >= 0; - if ((TypeTestUtil.isA(BigInteger.class, (ASTClassOrInterfaceType) type) - || jdk15 && TypeTestUtil.isA(BigDecimal.class, (ASTClassOrInterfaceType) type)) - && !node.hasDescendantOfType(ASTArrayDimsAndInits.class)) { - ASTArguments args = node.getFirstChildOfType(ASTArguments.class); - if (args.size() == 1) { - ASTLiteral literal = node.getFirstDescendantOfType(ASTLiteral.class); - if (literal == null - || literal.getParent().getParent().getParent().getParent().getParent() != args) { - return super.visit(node, data); - } + @NonNull + ASTArgumentList arguments = node.getArguments(); + if (arguments.size() == 1) { + ASTExpression firstArg = arguments.get(0); - String img = literal.getImage(); - if (literal.isStringLiteral()) { - img = img.substring(1, img.length() - 1); - } - - if ("0".equals(img) || "1".equals(img) || jdk15 && "10".equals(img)) { + Object constValue = firstArg.getConstValue(); + if (CONSTANTS.contains(constValue) + || jdk15 && "10".equals(constValue) + || jdk9 && "2".equals(constValue) + || Integer.valueOf(0).equals(constValue) + || Integer.valueOf(1).equals(constValue) + || jdk15 && Integer.valueOf(10).equals(constValue)) { addViolation(data, node); - return data; } } } - return super.visit(node, data); + return data; } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/BooleanInstantiationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/BooleanInstantiationRule.java deleted file mode 100644 index b8b345c071..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/BooleanInstantiationRule.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTArrayDimsAndInits; -import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; - -/** - * Avoid instantiating Boolean objects; you can reference Boolean.TRUE, - * Boolean.FALSE, or call Boolean.valueOf() instead. - * - *
- *  public class Foo {
- *       Boolean bar = new Boolean("true");    // just do a Boolean
- *       bar = Boolean.TRUE;                   //ok
- *       Boolean buz = Boolean.valueOf(false); // just do a Boolean buz = Boolean.FALSE;
- *  }
- * 
- */ -public class BooleanInstantiationRule extends AbstractJavaRule { - - /* - * see bug 1744065 : If somebody create it owns Boolean, the rule should not - * be triggered Therefore, we use this boolean to flag if the source code - * contains such an import - * - */ - private boolean customBoolean; - - @Override - public Object visit(ASTCompilationUnit decl, Object data) { - // customBoolean needs to be reset for each new file - customBoolean = false; - - return super.visit(decl, data); - } - - @Override - public Object visit(ASTImportDeclaration decl, Object data) { - // If the import actually import a Boolean class that overrides - // java.lang.Boolean - if (decl.getImportedName().endsWith("Boolean") && !"java.lang".equals(decl.getImportedName())) { - customBoolean = true; - } - return super.visit(decl, data); - } - - @Override - public Object visit(ASTAllocationExpression node, Object data) { - - if (!customBoolean) { - if (node.hasDescendantOfType(ASTArrayDimsAndInits.class)) { - return super.visit(node, data); - } - - ASTClassOrInterfaceType n1 = node.getFirstChildOfType(ASTClassOrInterfaceType.class); - if (TypeTestUtil.isA(Boolean.class, n1)) { - super.addViolation(data, node); - return data; - } - } - return super.visit(node, data); - } - - @Override - public Object visit(ASTPrimaryPrefix node, Object data) { - - if (!customBoolean) { - if (node.getNumChildren() == 0 || !(node.getChild(0) instanceof ASTName)) { - return super.visit(node, data); - } - - if ("Boolean.valueOf".equals(((ASTName) node.getChild(0)).getImage()) - || "java.lang.Boolean.valueOf".equals(((ASTName) node.getChild(0)).getImage())) { - ASTPrimaryExpression parent = (ASTPrimaryExpression) node.getParent(); - ASTPrimarySuffix suffix = parent.getFirstDescendantOfType(ASTPrimarySuffix.class); - if (suffix == null) { - return super.visit(node, data); - } - ASTPrimaryPrefix prefix = suffix.getFirstDescendantOfType(ASTPrimaryPrefix.class); - if (prefix == null) { - return super.visit(node, data); - } - - if (prefix.hasDescendantOfType(ASTBooleanLiteral.class)) { - super.addViolation(data, node); - return data; - } - ASTLiteral literal = prefix.getFirstDescendantOfType(ASTLiteral.class); - if (literal != null - && ("\"true\"".equals(literal.getImage()) || "\"false\"".equals(literal.getImage()))) { - super.addViolation(data, node); - return data; - } - } - } - return super.visit(node, data); - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InsufficientStringBufferDeclarationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InsufficientStringBufferDeclarationRule.java index 072a8c68df..44d11f3a30 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InsufficientStringBufferDeclarationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InsufficientStringBufferDeclarationRule.java @@ -187,7 +187,7 @@ public class InsufficientStringBufferDeclarationRule extends AbstractJavaRulecha if (ifStatement != null) { if (ifStatement.getThenBranch().descendants().any(n -> n == methodCall)) { state.addBranch(ifStatement.getThenBranch(), counter); - } else { + } else if (ifStatement.getElseBranch() != null) { state.addBranch(ifStatement.getElseBranch(), counter); } } else if (switchStatement != null) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UnnecessaryWrapperObjectCreationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UnnecessaryWrapperObjectCreationRule.java deleted file mode 100644 index a1ef669f08..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UnnecessaryWrapperObjectCreationRule.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import static net.sourceforge.pmd.util.CollectionUtil.setOf; - -import java.util.Set; - -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class UnnecessaryWrapperObjectCreationRule extends AbstractJavaRule { - - private static final Set PREFIX_SET = setOf("Byte.valueOf", "Short.valueOf", - "Integer.valueOf", "Long.valueOf", "Float.valueOf", "Double.valueOf", "Character.valueOf"); - - private static final Set SUFFIX_SET = setOf("toString", "byteValue", - "shortValue", "intValue", "longValue", "floatValue", "doubleValue", "charValue"); - - @Override - public Object visit(ASTPrimaryPrefix node, Object data) { - if (node.getNumChildren() == 0 || !(node.getChild(0) instanceof ASTName)) { - return super.visit(node, data); - } - - String image = ((ASTName) node.getChild(0)).getImage(); - if (image.startsWith("java.lang.")) { - image = image.substring(10); - } - - boolean checkBoolean = node.getTextDocument().getLanguageVersion() - .compareTo(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.5")) >= 0; - - if (PREFIX_SET.contains(image) || checkBoolean && "Boolean.valueOf".equals(image)) { - ASTPrimaryExpression parent = (ASTPrimaryExpression) node.getParent(); - if (parent.getNumChildren() >= 3) { - Node n = parent.getChild(2); - if (n instanceof ASTPrimarySuffix) { - ASTPrimarySuffix suffix = (ASTPrimarySuffix) n; - image = suffix.getImage(); - - if (SUFFIX_SET.contains(image) || checkBoolean && "booleanValue".equals(image)) { - super.addViolation(data, node); - return data; - } - } - } - } - return super.visit(node, data); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/security/AbstractHardCodedConstructorArgsVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/security/AbstractHardCodedConstructorArgsVisitor.java index 9bd35be043..beb50b5886 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/security/AbstractHardCodedConstructorArgsVisitor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/security/AbstractHardCodedConstructorArgsVisitor.java @@ -7,8 +7,11 @@ package net.sourceforge.pmd.lang.java.rule.security; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTArrayAllocation; import net.sourceforge.pmd.lang.java.ast.ASTArrayInitializer; +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.AccessType; +import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression; import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; @@ -46,27 +49,47 @@ abstract class AbstractHardCodedConstructorArgsVisitor extends AbstractJavaRulec return; } - // named variable - if (firstArgumentExpression instanceof ASTVariableAccess) { - ASTVariableAccess varAccess = (ASTVariableAccess) firstArgumentExpression; - if (varAccess.getSignature() != null && varAccess.getSignature().getSymbol() != null) { - ASTVariableDeclaratorId varDecl = varAccess.getSignature().getSymbol().tryGetNode(); - validateProperKeyArgument(data, varDecl.getInitializer()); + ASTVariableAccess varAccess = null; + + if (firstArgumentExpression instanceof ASTMethodCall) { + // check for method call on a named variable + ASTExpression expr = ((ASTMethodCall) firstArgumentExpression).getQualifier(); + if (expr instanceof ASTVariableAccess) { + varAccess = (ASTVariableAccess) expr; } + } else if (firstArgumentExpression instanceof ASTVariableAccess) { + // check for named variable + varAccess = (ASTVariableAccess) firstArgumentExpression; } - // hard coded array - if (firstArgumentExpression instanceof ASTArrayAllocation) { + if (varAccess != null && varAccess.getSignature() != null && varAccess.getSignature().getSymbol() != null) { + // named variable or method call on named variable found + ASTVariableDeclaratorId varDecl = varAccess.getSignature().getSymbol().tryGetNode(); + validateProperKeyArgument(data, varDecl.getInitializer()); + validateVarUsages(data, varDecl); + } else if (firstArgumentExpression instanceof ASTArrayAllocation) { + // hard coded array ASTArrayInitializer arrayInit = ((ASTArrayAllocation) firstArgumentExpression).getArrayInitializer(); if (arrayInit != null) { addViolation(data, arrayInit); } - } - - // string literal - ASTStringLiteral literal = firstArgumentExpression.descendants(ASTStringLiteral.class).first(); - if (literal != null) { - addViolation(data, literal); + } else { + // string literal + ASTStringLiteral literal = firstArgumentExpression.descendantsOrSelf() + .filterIs(ASTStringLiteral.class).first(); + if (literal != null) { + addViolation(data, literal); + } } } + + private void validateVarUsages(Object data, ASTVariableDeclaratorId varDecl) { + varDecl.getLocalUsages().stream() + .filter(u -> u.getAccessType() == AccessType.WRITE) + .filter(u -> u.getParent() instanceof ASTAssignmentExpression) + .forEach(usage -> { + ASTAssignmentExpression assignment = (ASTAssignmentExpression) usage.getParent(); + validateProperKeyArgument(data, assignment.getRightOperand()); + }); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java index 933646f934..d5a018fdcf 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java @@ -166,6 +166,7 @@ public interface JClassSymbol extends JTypeDeclSymbol, * Returns a set with all enum constant names. If this symbol does * not represent an enum, returns null. */ + @SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull") default @Nullable Set getEnumConstantNames() { return null; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstLocalVarSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstLocalVarSym.java index 06f164617a..d882787d41 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstLocalVarSym.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/ast/AstLocalVarSym.java @@ -14,7 +14,7 @@ import net.sourceforge.pmd.lang.java.types.Substitution; /** * @author Clément Fournier */ -final class AstLocalVarSym extends AbstractAstVariableSym implements JLocalVariableSymbol { +public final class AstLocalVarSym extends AbstractAstVariableSym implements JLocalVariableSymbol { AstLocalVarSym(ASTVariableDeclaratorId node, AstSymFactory factory) { super(node, factory); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/JavaSemanticErrors.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/JavaSemanticErrors.java index 284acfcab7..9b38f55923 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/JavaSemanticErrors.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/JavaSemanticErrors.java @@ -57,7 +57,7 @@ public final class JavaSemanticErrors { /** * TODO Should be an error. */ - public static final String MALFORMED_GENERIC_TYPE = "Maformed generic type: expected {0} type arguments, got {1}"; + public static final String MALFORMED_GENERIC_TYPE = "Malformed generic type: expected {0} type arguments, got {1}"; // this is an error public static final String EXPECTED_ANNOTATION_TYPE = "Expected an annotation type"; /** diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Substitution.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Substitution.java index 53cd551a49..d00f3804fa 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Substitution.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Substitution.java @@ -15,7 +15,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.function.Function; -import org.apache.commons.lang3.Validate; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.internal.util.AssertionUtil; @@ -101,16 +100,21 @@ public final class Substitution extends MapFunction<@NonNull SubstVar, @NonNull /** * Builds a substitution where the mapping from vars to types is * defined by the correspondence between the two lists. + *

+ * If there are no vars to be mapped, then no substitution is returned + * even though some types might have been supplied. * * @throws IllegalArgumentException If the two lists are of different lengths * @throws NullPointerException If any of the two lists is null */ public static Substitution mapping(List from, List to) { - if (from != null && from.isEmpty()) { - AssertionUtil.requireParamNotNull("to", to); - Validate.isTrue(to.isEmpty(), "Mismatched list sizes %s to %s", from, to); + AssertionUtil.requireParamNotNull("from", from); + AssertionUtil.requireParamNotNull("to", to); + + if (from.isEmpty()) { return EMPTY; } + // zip throws IllegalArgumentException if the lists are of different lengths return new Substitution(zip(from, to)); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypePrettyPrint.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypePrettyPrint.java index f21c782d4a..e3bdbd9358 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypePrettyPrint.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypePrettyPrint.java @@ -31,6 +31,10 @@ public final class TypePrettyPrint { return prettyPrint(t, new TypePrettyPrinter()); } + public static @NonNull String prettyPrintWithSimpleNames(@NonNull JTypeVisitable t) { + return prettyPrint(t, new TypePrettyPrinter().useSimpleNames(true)); + } + public static String prettyPrint(@NonNull JTypeVisitable t, TypePrettyPrinter prettyPrinter) { t.acceptVisitor(PrettyPrintVisitor.INSTANCE, prettyPrinter); return prettyPrinter.consumeResult(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 4b7017df95..98150320be 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -95,7 +95,7 @@ public final class TypeTestUtil { return isExactlyA(clazz, type.getSymbol()); } - return isA(type, otherType); + return isA(otherType, type); } @@ -134,26 +134,35 @@ public final class TypeTestUtil { return isA(canonicalName, thisType, null); } + public static boolean isA(@NonNull JTypeMirror t1, @Nullable TypeNode t2) { + return t2 != null && isA(t1, t2.getTypeMirror()); + } + /** - * This is the subtyping routine we use, which prunes some behavior - * of isSubtypeOf that we don't want (eg, that unresolved types are - * subtypes of everything). + * Checks whether the first type is a subtype of the second. This + * removes some behavior of isSubtypeOf that we don't want (eg, that + * unresolved types are subtypes of everything). + * + * @param t1 A supertype + * @param t2 A type + * + * @return Whether t1 is a subtype of t2 */ - private static boolean isA(JTypeMirror t1, JTypeMirror t2) { - if (t1 == null || t2 == null) { + private static boolean isA(@Nullable JTypeMirror t1, @NonNull JTypeMirror t2) { + if (t1 == null) { return false; - } else if (t1.isPrimitive() || t2.isPrimitive()) { - return t1.equals(t2); // isSubtypeOf considers primitive widening like subtyping - } else if (TypeOps.isUnresolved(t1)) { + } else if (t2.isPrimitive() || t1.isPrimitive()) { + return t2.equals(t1); // isSubtypeOf considers primitive widening like subtyping + } else if (TypeOps.isUnresolved(t2)) { // we can't get any useful info from this, isSubtypeOf would return true return false; - } else if (t2.isClassOrInterface() && ((JClassType) t2).getSymbol().isAnonymousClass()) { + } else if (t1.isClassOrInterface() && ((JClassType) t1).getSymbol().isAnonymousClass()) { return false; // conventionally - } else if (t1 instanceof JTypeVar) { - return t2.isTop() || isA(((JTypeVar) t1).getUpperBound(), t2); + } else if (t2 instanceof JTypeVar) { + return t1.isTop() || isA(t1, ((JTypeVar) t2).getUpperBound()); } - return t1.isSubtypeOf(t2); + return t2.isSubtypeOf(t1); } private static boolean isA(@NonNull String canonicalName, @NonNull JTypeMirror thisType, @Nullable UnresolvedClassStore unresolvedStore) { @@ -173,7 +182,7 @@ public final class TypeTestUtil { TypeSystem ts = thisType.getTypeSystem(); @Nullable JTypeMirror otherType = TypesFromReflection.loadType(ts, canonicalName, unresolvedStore); - return isA(thisType, otherType); + return isA(otherType, thisType); } /** diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/SupertypeCheckCache.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/SupertypeCheckCache.java index d70ac85a5d..e52511c321 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/SupertypeCheckCache.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/SupertypeCheckCache.java @@ -41,7 +41,7 @@ final class SupertypeCheckCache { } void remember(JTypeMirror t, JTypeMirror s) { - if (shouldCache(t)) { + if (shouldCache(t) && shouldCache(s)) { cache.computeIfAbsent(t, k -> new HashSet<>()).add(s); } } diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 645c321a74..3a2e29b77a 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -802,7 +802,6 @@ public class MyTest { since="6.35.0" message="JUnit 5 tests should be package-private." class="net.sourceforge.pmd.lang.rule.XPathRule" - typeResolution="true" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#junit5testshouldbepackageprivate"> + + + Reports usages of primitive wrapper constructors. They are deprecated + since Java 9 and should not be used. Even before Java 9, they can + be replaced with usage of the corresponding static `valueOf` factory method + (which may be automatically inserted by the compiler since Java 1.5). + This has the advantage that it may reuse common instances instead of creating + a new instance each time. + + Note that for `Boolean`, the named constants `Boolean.TRUE` and `Boolean.FALSE` + are preferred instead of `Boolean.valueOf`. + + 3 + + + + + + + + + Reports test assertions that may be simplified using a more specific + assertion method. This enables better error messages, and makes the + assertions more readable. + + 3 + + + + + - - -This rule detects JUnit assertions in object equality. These assertions should be made by more specific methods, like assertEquals. - - 3 - - - - - - - - - - - - - - -This rule detects JUnit assertions in object references equality. These assertions should be made by -more specific methods, like assertNull, assertNotNull. - - 3 - - - - - - - - - - - - - - -This rule detects JUnit assertions in object references equality. These assertions should be made -by more specific methods, like assertSame, assertNotSame. - - 3 - - - - - - - - - - - - - - -When asserting a value is the same as a literal or Boxed boolean, use assertTrue/assertFalse, instead of assertEquals. - - 3 - - - - - - - - - - - - Java 7 introduced the try-with-resources statement. This statement ensures that each resource is closed at the end @@ -1826,21 +1735,6 @@ the try block was suppressed. With the `try`-with-resources statement, the excep preserved. 3 - - - - - - - - 1 @@ -403,54 +411,6 @@ while (true) { // preferred approach - - -Use explicit scoping instead of accidental usage of default package private level. -The rule allows methods and fields annotated with Guava's @VisibleForTesting and JUnit 5's annotations. - -This rule is deprecated since PMD 6.35.0. It assumes that any usage of package-access is accidental, -and by doing so, prohibits using a really fundamental and useful feature of the language. - -To satisfy the rule, you have to make the member public even if it doesn't need to, or make it protected, -which muddies your intent even more if you don't intend the class to be extended, and may be at odds with -other rules like {% rule "java/codestyle/AvoidProtectedFieldInFinalClass" %}. - -The rule {% rule "java/codestyle/CommentDefaultAccessModifier" %} should be used instead. This rule flags -the same thing, but has an escape hatch. - - 3 - - - - - - - - - + + + Reports explicit boxing and unboxing conversions that may safely be removed, + either because they would be inserted by the compiler automatically, + or because they're semantically a noop (eg unboxing a value to rebox it immediately). + + Note that this only handles boxing and unboxing conversions occurring through + calls to `valueOf` or one of the `intValue`, `byteValue`, etc. methods. Casts + that command a conversion are reported by {% rule UnnecessaryCast %} instead. + + 3 + + + + -A method or constructor should not explicitly declare unchecked exceptions in its -`throws` clause. Java doesn't force the caller to handle an unchecked exception, +Reports unchecked exceptions in the `throws` clause of a method or constructor. +Java doesn't force the caller to handle an unchecked exception, so it's unnecessary except for documentation. A better practice is to document the exceptional cases with a `@throws` Javadoc tag, which allows being more descriptive. @@ -296,9 +296,7 @@ exceptional cases with a `@throws` Javadoc tag, which allows being more descript @@ -314,27 +312,15 @@ public void foo() throws RuntimeException { -A class with only private constructors should be final, unless the private constructor -is invoked by a inner class. +Reports classes that may be made final because they cannot be extended from outside +their compilation unit anyway. This is because all their constructors are private, +so a subclass could not call the super constructor. 1 - - - - - - - - -Sometimes two consecutive 'if' statements can be consolidated by separating their conditions with a boolean short-circuit operator. - + 3 @@ -797,12 +784,11 @@ in each object at runtime. @@ -940,7 +926,7 @@ Use opposite operator instead of negating the whole expression with a logic comp ', '<=', '>=')] ]]> @@ -1112,12 +1098,12 @@ public void foo() throws Exception { @@ -1163,62 +1147,6 @@ public class Foo { - - -Avoid negation in an assertTrue or assertFalse test. - -For example, rephrase: - - assertTrue(!expr); - -as: - - assertFalse(expr); - - - 3 - - - - - - - - - - - No need to check for null before an instanceof; the instanceof keyword returns false when given a null argument. 3 - - - - - - - @@ -56,73 +56,73 @@ public class StaticField { -Methods such as getDeclaredConstructors(), getDeclaredConstructor(Class[]) and setAccessible(), -as the interface PrivilegedAction, allow for the runtime alteration of variable, class, or -method visibility, even if they are private. This violates the principle of encapsulation. +Methods such as `getDeclaredConstructors()`, `getDeclaredMethods()`, and `getDeclaredFields()` also +return private constructors, methods and fields. These can be made accessible by calling `setAccessible(true)`. +This gives access to normally protected data which violates the principle of encapsulation. + +This rule detects calls to `setAccessible` and finds possible accessibility alterations. +If the call to `setAccessible` is wrapped within a `PrivilegedAction`, then the access alteration +is assumed to be deliberate and is not reported. + +Note that with Java 17 the Security Manager, which is used for `PrivilegedAction` execution, +is deprecated: [JEP 411: Deprecate the Security Manager for Removal](https://openjdk.java.net/jeps/411). +For future-proof code, deliberate access alteration should be suppressed using the usual +suppression methods (e.g. by using `@SuppressWarnings` annotation). 3 constructor = this.getClass().getDeclaredConstructor(String.class); + // call to forbidden setAccessible + constructor.setAccessible(true); - // Possible call to forbidden PrivilegedAction - PrivilegedAction priv = (PrivilegedAction) new Object(); priv.run(); - } + Method privateMethod = this.getClass().getDeclaredMethod("aPrivateMethod"); + // call to forbidden setAccessible + privateMethod.setAccessible(true); + + // deliberate accessibility alteration + String privateField = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public String run() { + try { + Field field = Violation.class.getDeclaredField("aPrivateField"); + field.setAccessible(true); + return (String) field.get(null); + } catch (ReflectiveOperationException | SecurityException e) { + throw new RuntimeException(e); + } + } + }); + } } ]]> @@ -422,12 +422,9 @@ Each caught exception type should be handled in its own catch clause. @@ -637,8 +634,6 @@ k = i * j; // set k with 80 not 120 - - When deriving an array of a specific class from your Collection, one should provide an array of -the same class as the parameter of the toArray() method. Doing otherwise you will will result -in a ClassCastException. +the same class as the parameter of the `toArray()` method. Doing otherwise will result +in a `ClassCastException`. 3 @@ -852,7 +844,7 @@ c.add(obj); Integer[] a = (Integer [])c.toArray(); // this is fine and will not trigger the rule -Integer[] b = (Integer [])c.toArray(new Integer[c.size()]); +Integer[] b = (Integer [])c.toArray(new Integer[0]); ]]> @@ -864,7 +856,7 @@ Integer[] b = (Integer [])c.toArray(new Integer[c.size()]); class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#clonemethodmustbepublic"> -The java Manual says "By convention, classes that implement this interface should override +The java manual says "By convention, classes that implement this interface should override Object.clone (which is protected) with a public method." 3 @@ -872,9 +864,9 @@ Object.clone (which is protected) with a public method." @@ -933,10 +925,10 @@ public class MyClass { class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#clonemethodreturntypemustmatchclassname"> -If a class implements cloneable the return type of the method clone() must be the class name. That way, the caller +If a class implements `Cloneable` the return type of the method `clone()` must be the class name. That way, the caller of the clone method doesn't need to cast the returned clone to the correct type. -Note: This is only possible with Java 1.5 or higher. +Note: Such a covariant return type is only possible with Java 1.5 or higher. 3 @@ -944,11 +936,9 @@ Note: This is only possible with Java 1.5 or higher. @@ -970,53 +960,6 @@ public class Foo implements Cloneable { - - -The method clone() should throw a CloneNotSupportedException. - -This rule is deprecated since PMD 6.35.0 without replacement. The rule has no real value as -`CloneNotSupportedException` is a checked exception and therefore you need to deal with it while -implementing the `clone()` method. You either need to declare the exception or catch it. If you catch it, -then subclasses can't throw it themselves explicitly. However, `Object.clone()` will still throw this -exception if the `Cloneable` interface is not implemented. - - 3 - - - - - - - - - - - - =", "<", ">")]/FieldAccess[@Name='NaN' and (pmd-java:typeIs('double') or pmd-java:typeIs('float'))] ]]> @@ -1260,9 +1203,11 @@ public class MyTest { class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#donotcallgarbagecollectionexplicitly"> -Calls to System.gc(), Runtime.getRuntime().gc(), and System.runFinalization() are not advised. Code should have the -same behavior whether the garbage collection is disabled using the option -Xdisableexplicitgc or not. -Moreover, "modern" jvms do a very good job handling garbage collections. If memory usage issues unrelated to memory +Calls to `System.gc()`, `Runtime.getRuntime().gc()`, and `System.runFinalization()` are not advised. +Code should have the same behavior whether the garbage collection is disabled using the option +`-Xdisableexplicitgc` or not. + +Moreover, "modern" JVMs do a very good job handling garbage collections. If memory usage issues unrelated to memory leaks develop within an application, it should be dealt with JVM options rather than within the code itself. 2 @@ -1270,14 +1215,11 @@ leaks develop within an application, it should be dealt with JVM options rather @@ -1445,12 +1387,27 @@ public class Foo { language="java" since="1.5" message="Avoid importing anything from the 'sun.*' packages" - class="net.sourceforge.pmd.lang.java.rule.errorprone.DontImportSunRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#dontimportsun"> -Avoid importing anything from the 'sun.*' packages. These packages are not portable and are likely to change. +Avoid importing anything from the 'sun.*' packages. These packages are not portable +and are likely to change. + +If you find yourself having to depend on Sun APIs, confine this dependency to as +small a scope as possible, for instance by writing a stable wrapper class around +the unstable API. You can then suppress this rule in the implementation of the wrapper. 4 + + + + + + + + @@ -1905,20 +1861,11 @@ If the finalize() is implemented, its last action should be to call super.finali @@ -1948,12 +1895,7 @@ If the finalize() is implemented, it should do something besides just calling su @@ -2017,7 +1959,7 @@ Note that Oracle has declared Object.finalize() as deprecated since JDK 9. @@ -2053,6 +1995,47 @@ public class Foo { + + +Switch statements without break or return statements for each case option +may indicate problematic behaviour. Empty cases are ignored as these indicate +an intentional fall-through. + +You can ignore a violation by commenting `// fallthrough` before the case label +which is reached by fallthrough, or with `@SuppressWarnings("fallthrough")`. + +This rule has been renamed from "MissingBreakInSwitch" with PMD 6.37.0. + + 3 + + + + + @@ -2088,10 +2068,6 @@ Class c = String.class; - - -Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional. +Avoid jumbled loop incrementers - it's usually a mistake, and is confusing even if intentional. 3 @@ -2229,37 +2205,41 @@ public class MyClass { class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#misplacednullcheck"> -The null check here is misplaced. If the variable is null a NullPointerException will be thrown. -Either the check is useless (the variable will never be "null") or it is incorrect. +The null check here is misplaced. If the variable is null a `NullPointerException` will be thrown. +Either the check is useless (the variable will never be `null`) or it is incorrect. 3 @@ -2288,41 +2268,6 @@ public class Foo { - - -Switch statements without break or return statements for each case option -may indicate problematic behaviour. Empty cases are ignored as these indicate -an intentional fall-through. - -You can ignore a violation by commenting `// fallthrough` before the case label -which is reached by fallthrough, or with `@SuppressWarnings("fallthrough")`. - - 3 - - - - - @@ -2380,35 +2324,31 @@ See the property `annotations`. A non-case label (e.g. a named break/continue label) was present in a switch statement. -This legal, but confusing. It is easy to mix up the case labels and the non-case labels. +This is legal, but confusing. It is easy to mix up the case labels and the non-case labels. 3 - //SwitchStatement//BlockStatement/Statement/LabeledStatement + //SwitchStatement//LabeledStatement @@ -2523,7 +2463,7 @@ confusing. @@ -2640,36 +2580,32 @@ with the restriction that the logger needs to be passed into the constructor. - + - + message="Return an empty collection rather than 'null'." + externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#returnemptycollectionratherthannull"> -For any method that returns an array, it is a better to return an empty array rather than a -null reference. This removes the need for null checking all results and avoids inadvertent -NullPointerExceptions. +For any method that returns an collection (such as an array, Collection or Map), it is better to return +an empty one rather than a null reference. This removes the need for null checking all results and avoids +inadvertent NullPointerExceptions. + +See Effective Java, 3rd Edition, Item 54: Return empty collections or arrays instead of null 1 + @@ -2740,7 +2676,7 @@ Avoid returning from a finally block, this can discard exceptions. 3 - //FinallyStatement//ReturnStatement except //FinallyStatement//(MethodDeclaration|LambdaExpression)//ReturnStatement + //FinallyClause//ReturnStatement except //FinallyClause//(MethodDeclaration|LambdaExpression)//ReturnStatement @@ -2775,9 +2711,9 @@ formatting is used. @@ -2867,25 +2803,16 @@ behavior especially when instances are distributed by the container on several J @@ -2931,13 +2858,8 @@ new StringBuilder("A") // 1 + 16 = 17 @@ -2975,24 +2897,20 @@ Item 10: Obey the general contract when overriding equals. @@ -3019,12 +2937,26 @@ public class Foo { language="java" since="1.5" message="The method name and return type are suspiciously close to hashCode()" - class="net.sourceforge.pmd.lang.java.rule.errorprone.SuspiciousHashcodeMethodNameRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#suspicioushashcodemethodname"> The method name and return type are suspiciously close to hashCode(), which may denote an intention to override the hashCode() method. + + + + + + + 3 @@ -3104,9 +3036,7 @@ Do not use "if" statements whose conditionals are always true or always false. @@ -3132,37 +3062,32 @@ public class Foo { externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#unnecessarybooleanassertion"> A JUnit test assertion with a boolean literal is unnecessary since it always will evaluate to the same thing. -Consider using flow control (in case of assertTrue(false) or similar) or simply removing -statements like assertTrue(true) and assertFalse(false). If you just want a test to halt after finding -an error, use the fail() method and provide an indication message of why it did. +Consider using flow control (in case of `assertTrue(false)` or similar) or simply removing +statements like `assertTrue(true)` and `assertFalse(false)`. If you just want a test to halt after finding +an error, use the `fail()` method and provide an indication message of why it did. 3 @@ -3171,7 +3096,7 @@ an error, use the fail() method and provide an indication message of why it did. @@ -3201,13 +3126,32 @@ boolean answer2 = buz.toUpperCase().equalsIgnoreCase("baz"); // another unnec language="java" since="0.1" message="Avoid unnecessary temporaries when converting primitives to Strings" - class="net.sourceforge.pmd.lang.java.rule.errorprone.UnnecessaryConversionTemporaryRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#unnecessaryconversiontemporary"> Avoid the use temporary objects when converting primitives to Strings. Use the static conversion methods on the wrapper classes instead. 3 + + + + + + + -After checking an object reference for null, you should invoke equals() on that object rather than passing it to another object's equals() method. +After checking an object reference for null, you should invoke equals() on that object rather than passing +it to another object's equals() method. 3 @@ -3293,23 +3236,17 @@ public class Test { externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#usecorrectexceptionlogging"> To make sure the full stacktrace is printed out, use the logging statement with two arguments: a String and a Throwable. + +This rule only applies to [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/). 3 @@ -3338,16 +3275,18 @@ public class Main { class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#useequalstocomparestrings"> -Using '==' or '!=' to compare strings only works if intern version is used on both sides. -Use the equals() method instead. +Using '==' or '!=' to compare strings is only reliable if the interned string (`String#intern()`) +is used on both sides. + +Use the `equals()` method instead. 3 @@ -3399,38 +3338,29 @@ class Test { class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#uselocalewithcaseconversions"> - When doing `String::toLowerCase()/toUpperCase()` conversions, use an explicit locale argument to specify the case transformation rules. +When doing `String::toLowerCase()/toUpperCase()` conversions, use an explicit locale argument to specify the case +transformation rules. - Using `String::toLowerCase()` without arguments implicitly uses `Locale::getDefault()`. - The problem is that the default locale depends on the current JVM setup (and usually on the system in which it is running). - Using the system default may be exactly what you want (e.g. if you are manipulating strings you got through standard input), - but it may as well not be the case (e.g. if you are getting the string over the network or a file, and the encoding is well-defined - and independent of the environment). In the latter case, using the default locale makes the case transformation brittle, as - it may yield unexpected results on a machine whose locale has other case translation rules. For example, in Turkish, the - uppercase form of `i` is `İ` (U+0130, not ASCII) and not `I` (U+0049) as in English. +Using `String::toLowerCase()` without arguments implicitly uses `Locale::getDefault()`. +The problem is that the default locale depends on the current JVM setup (and usually on the system in which +it is running). Using the system default may be exactly what you want (e.g. if you are manipulating strings +you got through standard input), but it may as well not be the case (e.g. if you are getting the string over +the network or a file, and the encoding is well-defined and independent of the environment). In the latter case, +using the default locale makes the case transformation brittle, as it may yield unexpected results on a machine +whose locale has other case translation rules. For example, in Turkish, the uppercase form of `i` is `İ` (U+0130, +not ASCII) and not `I` (U+0049) as in English. - The rule is intended to *force* developers to think about locales when dealing with strings. By taking a conscious decision about - the choice of locale at the time of writing, you reduce the risk of surprising behaviour down the line, and communicate your intent - to future readers. +The rule is intended to *force* developers to think about locales when dealing with strings. By taking a +conscious decision about the choice of locale at the time of writing, you reduce the risk of surprising +behaviour down the line, and communicate your intent to future readers. 3 @@ -3468,7 +3398,7 @@ Thread.currentThread().getContextClassLoader() instead. 3 - //PrimarySuffix[@Image='getClassLoader'] | //PrimaryPrefix[ends-with(Name/@Image, '.getClassLoader')] + //MethodCall[pmd-java:matchesSig("java.lang.Class#getClassLoader()")] diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 7cc480a7ad..513d7810a5 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -24,7 +24,7 @@ It is much better to use one of the type-specific toString() methods instead. @@ -61,33 +61,33 @@ sb.append('a'); // use this instead -Instead of manually copying data between two arrays, use the efficient Arrays.copyOf or System.arraycopy method instead. +Instead of manually copying data between two arrays, use the more efficient `Arrays.copyOf` or `System.arraycopy` +methods instead. 3 @@ -98,12 +98,15 @@ public class Test { public void bar() { int[] a = new int[10]; int[] b = new int[10]; + + // instead of this loop for (int i=0;i<10;i++) { b[i]=a[i]; } + // use: System.arraycopy(a, 0, b, 0, 10); int[] c = new int[10]; - // this will trigger the rule + // this won't trigger the rule for (int i=0;i<10;i++) { b[i]=a[c[i]]; } @@ -116,12 +119,13 @@ public class Test { -Problem: A Calendar is a heavyweight object and expensive to create. +Problem: `java.util.Calendar` is a heavyweight object and expensive to create. It should only be used, if +calendar calculations are needed. Solution: Use `new Date()`, Java 8+ `java.time.LocalDateTime.now()` or `ZonedDateTime.now()`. @@ -129,26 +133,32 @@ Solution: Use `new Date()`, Java 8+ `java.time.LocalDateTime.now()` or `ZonedDat 2 and ../PrimarySuffix[last()-1][@Image = 'getTime' or @Image='getTimeInMillis']] +//MethodCall[pmd-java:matchesSig("java.util.Calendar#getTime()") or pmd-java:matchesSig("java.util.Calendar#getTimeInMillis()")] + [*[1][local-name() = ('MethodCall', 'ConstructorCall')] + [pmd-java:matchesSig("java.util.Calendar#getInstance()") + or pmd-java:matchesSig("java.util.GregorianCalendar#getInstance()") + or pmd-java:matchesSig("java.util.GregorianCalendar#new()")] + ] | -//MethodDeclaration[not(MethodDeclarator/FormalParameters//ClassOrInterfaceType[pmd-java:typeIs('java.util.Calendar')])] - /Block/BlockStatement//PrimaryExpression - /PrimaryPrefix/Name - [pmd-java:typeIs('java.util.Calendar')] - [every $var in @Image satisfies ( - (ends-with($var, '.getTime') or ends-with($var, '.getTimeInMillis')) - and - (: ignore if .set* or .add* or .clear is called on the variable :) - not(ancestor::Block/BlockStatement//Name[ - starts-with(@Image, concat((tokenize($var, '\.'), $var)[1], '.set')) - or - starts-with(@Image, concat((tokenize($var, '\.'), $var)[1], '.add')) - or - starts-with(@Image, concat((tokenize($var, '\.'), $var)[1], '.clear')) - ]) - )] +//MethodCall[pmd-java:matchesSig("java.util.Calendar#getTime()") or pmd-java:matchesSig("java.util.Calendar#getTimeInMillis()")] + [*[1][local-name() = 'VariableAccess']] + (: ignore if .set* or .add or .clear or .roll is called on the variable :) + [not(VariableAccess/@Name = ancestor::Block//MethodCall[starts-with(@MethodName, "set") or @MethodName = ("add", "clear", "roll")]/VariableAccess/@Name)] + (: variable must be initialized with getInstance :) + [VariableAccess/@Name = ancestor::Block//LocalVariableDeclaration/VariableDeclarator[ + (MethodCall | ConstructorCall) + [pmd-java:matchesSig("java.util.Calendar#getInstance()") + or pmd-java:matchesSig("java.util.GregorianCalendar#getInstance()") + or pmd-java:matchesSig("java.util.GregorianCalendar#new()")] + ]/VariableDeclaratorId/@Name] | -//ClassOrInterfaceType[pmd-java:typeIs('org.joda.time.DateTime') or pmd-java:typeIs('org.joda.time.LocalDateTime')][../Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name[ends-with(@Image, 'Calendar.getInstance')]] +//ConstructorCall[pmd-java:typeIs("org.joda.time.DateTime") or pmd-java:typeIs("org.joda.time.LocalDateTime")] + [ArgumentList[(MethodCall | ConstructorCall) + [pmd-java:matchesSig("java.util.Calendar#getInstance()") + or pmd-java:matchesSig("java.util.GregorianCalendar#getInstance()") + or pmd-java:matchesSig("java.util.GregorianCalendar#new()")]] + ] + ]]> @@ -265,118 +275,28 @@ public class Something { - - -Note: this rule is deprecated, as its rationale does not hold. - -Java uses the 'short' type to reduce memory usage, not to optimize calculation. In fact, the JVM does not have any -arithmetic capabilities for the short type: the JVM must convert the short into an int, do the proper calculation -and convert the int back to a short. Thus any storage gains found through use of the 'short' type may be offset by -adverse impacts on performance. - - 1 - - - - - - - - - - - - -Don't create instances of already existing BigInteger (BigInteger.ZERO, BigInteger.ONE) and -for Java 1.5 onwards, BigInteger.TEN and BigDecimal (BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.TEN) +Don't create instances of already existing BigInteger (`BigInteger.ZERO`, `BigInteger.ONE`), +for Java 1.5 onwards, BigInteger.TEN and BigDecimal (`BigDecimal.ZERO`, `BigDecimal.ONE`, `BigDecimal.TEN`) and +for Java 9 onwards `BigInteger.TWO`. 3 - - +BigInteger bi3; +bi3 = new BigInteger("0"); // reference BigInteger.ZERO instead - - -Avoid instantiating Boolean objects; you can reference Boolean.TRUE, Boolean.FALSE, or call Boolean.valueOf() instead. -Note that new Boolean() is deprecated since JDK 9 for that reason. - - 2 - - - - - - - -Calling new Byte() causes memory allocation that can be avoided by the static Byte.valueOf(). -It makes use of an internal cache that recycles earlier instances making it more memory efficient. -Note that new Byte() is deprecated since JDK 9 for that reason. - - 2 - - - - - - - - - @@ -525,66 +445,6 @@ good.append("This is a long string, which is pre-sized"); - - -Calling new Integer() causes memory allocation that can be avoided by the static Integer.valueOf(). -It makes use of an internal cache that recycles earlier instances making it more memory efficient. -Note that new Integer() is deprecated since JDK 9 for that reason. - - 2 - - - - - - - - - - - - - - -Calling new Long() causes memory allocation that can be avoided by the static Long.valueOf(). -It makes use of an internal cache that recycles earlier instances making it more memory efficient. -Note that new Long() is deprecated since JDK 9 for that reason. - - 2 - - - - - - - - - - - - - - -Note: this rule is deprecated for removal, as the optimization is insignificant. - -Calls to `string.startsWith("x")` with a string literal of length 1 can be rewritten using `string.charAt(0)`, -at the expense of some readability. To prevent `IndexOutOfBoundsException` being thrown by the `charAt` method, -ensure that the string is not empty by making an additional check first. - - 3 - - - - - - - - - - - - - - -Calling new Short() causes memory allocation that can be avoided by the static Short.valueOf(). -It makes use of an internal cache that recycles earlier instances making it more memory efficient. -Note that new Short() is deprecated since JDK 9 for that reason. - - 2 - - - - - - - - - - - - - - -Most wrapper classes provide static conversion methods that avoid the need to create intermediate objects -just to create the primitive forms. Using these avoids the cost of creating objects that also need to be -garbage-collected later. - - 3 - - - - - - + diff --git a/pmd-java/src/main/resources/rulesets/java/clone.xml b/pmd-java/src/main/resources/rulesets/java/clone.xml index f662ff5473..ec7dc50558 100644 --- a/pmd-java/src/main/resources/rulesets/java/clone.xml +++ b/pmd-java/src/main/resources/rulesets/java/clone.xml @@ -12,7 +12,7 @@ The Clone Implementation ruleset contains a collection of rules that find questi - + - \ No newline at end of file + diff --git a/pmd-java/src/main/resources/rulesets/java/controversial.xml b/pmd-java/src/main/resources/rulesets/java/controversial.xml index 5b2be135bd..1efb0be7c9 100644 --- a/pmd-java/src/main/resources/rulesets/java/controversial.xml +++ b/pmd-java/src/main/resources/rulesets/java/controversial.xml @@ -24,7 +24,7 @@ They are held here to allow people to include them as they see fit within their - + @@ -33,7 +33,7 @@ They are held here to allow people to include them as they see fit within their - + diff --git a/pmd-java/src/main/resources/rulesets/java/design.xml b/pmd-java/src/main/resources/rulesets/java/design.xml index 9ac4ae2fda..e2bde4ae93 100644 --- a/pmd-java/src/main/resources/rulesets/java/design.xml +++ b/pmd-java/src/main/resources/rulesets/java/design.xml @@ -28,18 +28,18 @@ are suggested. - + - + - + diff --git a/pmd-java/src/main/resources/rulesets/java/junit.xml b/pmd-java/src/main/resources/rulesets/java/junit.xml index d122e26f81..057cd7f536 100644 --- a/pmd-java/src/main/resources/rulesets/java/junit.xml +++ b/pmd-java/src/main/resources/rulesets/java/junit.xml @@ -17,10 +17,10 @@ These rules deal with different problems that can occur with JUnit tests. - - - - + + + + - + diff --git a/pmd-java/src/main/resources/rulesets/java/logging-java.xml b/pmd-java/src/main/resources/rulesets/java/logging-java.xml index e4882f5f14..65858b65d8 100644 --- a/pmd-java/src/main/resources/rulesets/java/logging-java.xml +++ b/pmd-java/src/main/resources/rulesets/java/logging-java.xml @@ -10,7 +10,7 @@ The Java Logging ruleset contains a collection of rules that find questionable u - + diff --git a/pmd-java/src/main/resources/rulesets/java/migrating.xml b/pmd-java/src/main/resources/rulesets/java/migrating.xml index bc7681960a..9046281651 100644 --- a/pmd-java/src/main/resources/rulesets/java/migrating.xml +++ b/pmd-java/src/main/resources/rulesets/java/migrating.xml @@ -24,9 +24,9 @@ rather, use a wrapper ruleset such as migrating_to_13.xml. - - - - + + + + diff --git a/pmd-java/src/main/resources/rulesets/java/migrating_to_15.xml b/pmd-java/src/main/resources/rulesets/java/migrating_to_15.xml index 58f833176f..a1514ce199 100644 --- a/pmd-java/src/main/resources/rulesets/java/migrating_to_15.xml +++ b/pmd-java/src/main/resources/rulesets/java/migrating_to_15.xml @@ -9,9 +9,9 @@ Contains rules for migrating to JDK 1.5 - - - - + + + + diff --git a/pmd-java/src/main/resources/rulesets/java/optimizations.xml b/pmd-java/src/main/resources/rulesets/java/optimizations.xml index 4439c5385e..ec6d63d4f0 100644 --- a/pmd-java/src/main/resources/rulesets/java/optimizations.xml +++ b/pmd-java/src/main/resources/rulesets/java/optimizations.xml @@ -17,10 +17,10 @@ These rules deal with different optimizations that generally apply to best pract - - + + - \ No newline at end of file + diff --git a/pmd-java/src/main/resources/rulesets/java/quickstart.xml b/pmd-java/src/main/resources/rulesets/java/quickstart.xml index cf1d625ecb..eca04a16fe 100644 --- a/pmd-java/src/main/resources/rulesets/java/quickstart.xml +++ b/pmd-java/src/main/resources/rulesets/java/quickstart.xml @@ -37,10 +37,12 @@ + + @@ -48,10 +50,6 @@ - - - - @@ -90,7 +88,6 @@ - @@ -104,6 +101,7 @@ + @@ -153,7 +151,6 @@ - @@ -225,6 +222,7 @@ + @@ -233,7 +231,6 @@ - @@ -243,7 +240,7 @@ - + @@ -297,25 +294,17 @@ - - - - - - - - diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java index 6079eb9067..286f5bc959 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -42,24 +42,20 @@ public class LanguageVersionTest extends AbstractLanguageVersionTest { LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("11"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "12", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("12"), }, - { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "12-preview", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("12-preview"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "13", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("13"), }, - { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "13-preview", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("13-preview"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "14", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("14"), }, - { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "14-preview", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("14-preview"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "15", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15"), }, - { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "15-preview", - LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("15-preview"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "16", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("16"), }, { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "16-preview", LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("16-preview"), }, + { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "17", + LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("17"), }, + { JavaLanguageModule.NAME, JavaLanguageModule.TERSE_NAME, "17-preview", + LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("17-preview"), }, // this one won't be found: case sensitive! { "JAVA", "JAVA", "1.7", null, }, diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java index 2fc217381d..87662cd50e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java @@ -37,6 +37,12 @@ public class CLITest extends BaseCLITest { runTest(args, "minimalArgsWithDebug"); } + @Test + public void usingDebugLongOption() { + String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "--debug", }; + runTest(args, "minimalArgsWithDebug"); + } + @Test public void changeJavaVersion() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "-version", "1.5", "-language", @@ -66,6 +72,13 @@ public class CLITest extends BaseCLITest { assertTrue(FileUtil.findPatternInFile(new File(resultFilename), "Avoid empty if")); } + @Test + public void exitStatusWithViolationsAndWithoutFailOnViolationsLongOption() { + String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/errorprone.xml", "--fail-on-violation", "false", }; + String resultFilename = runTest(args, "exitStatusWithViolationsAndWithoutFailOnViolations", 0); + assertTrue(FileUtil.findPatternInFile(new File(resultFilename), "Avoid empty if")); + } + /** * See https://sourceforge.net/p/pmd/bugs/1231/ */ diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java index 895622d698..99f534b543 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java @@ -11,35 +11,51 @@ import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.StandardErrorStreamLog; -import org.junit.contrib.java.lang.system.StandardOutputStreamLog; +import org.junit.contrib.java.lang.system.SystemErrRule; +import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.rules.TemporaryFolder; import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.java.JavaLanguageModule; public class PMDCoverageTest { @Rule - public StandardOutputStreamLog output = new StandardOutputStreamLog(); + public SystemOutRule output = new SystemOutRule().muteForSuccessfulTests().enableLog(); @Rule - public StandardErrorStreamLog errorStream = new StandardErrorStreamLog(); + public SystemErrRule errorStream = new SystemErrRule().muteForSuccessfulTests().enableLog(); @Rule public TemporaryFolder folder = new TemporaryFolder(); - /** - * Test some of the PMD command line options - */ @Test public void testPmdOptions() { - runPmd("-d src/main/java/net/sourceforge/pmd/lang/java/rule/design -f text -R rulesets/internal/all-java.xml -language java -stress -benchmark"); + runPmd("-d src/main/java/net/sourceforge/pmd/lang/java/rule/design -f text -R rulesets/internal/all-java.xml -stress -benchmark"); + } + + + @Test + public void runAllJavaPmdOnSourceTree() { + runPmd("-d src/main/java -f text -R rulesets/internal/all-java.xml"); + } + + @Test + public void runAllJavaPmdOnTestResourcesWithLatestJavaVersion() { + List versions = LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersions(); + LanguageVersion latest = versions.get(versions.size() - 1); + + runPmd("-d src/test/resources -f text -R rulesets/internal/all-java.xml -language java -version " + latest.getVersion()); } /** @@ -49,8 +65,10 @@ public class PMDCoverageTest { */ private void runPmd(String commandLine) { String[] args = commandLine.split("\\s"); + String report = "missing report"; try { + File f = folder.newFile(); args = ArrayUtils.addAll( args, @@ -60,27 +78,26 @@ public class PMDCoverageTest { String.valueOf(Runtime.getRuntime().availableProcessors()) ); + System.err.println("Running PMD with: " + Arrays.toString(args)); PMD.runPmd(args); + report = FileUtils.readFileToString(f, StandardCharsets.UTF_8); assertEquals("Nothing should be output to stdout", 0, output.getLog().length()); - assertEquals("No exceptions expected", 0, StringUtils.countMatches(errorStream.getLog(), "Exception applying rule")); assertFalse("Wrong configuration? Ruleset not found", errorStream.getLog().contains("Ruleset not found")); assertEquals("No usage of deprecated XPath attributes expected", 0, StringUtils.countMatches(errorStream.getLog(), "Use of deprecated attribute")); - String report = FileUtils.readFileToString(f, StandardCharsets.UTF_8); assertEquals("No processing errors expected", 0, StringUtils.countMatches(report, "Error while processing")); // we might have explicit examples of parsing errors, so these are maybe false positives assertEquals("No parsing error expected", 0, StringUtils.countMatches(report, "Error while parsing")); } catch (IOException ioe) { fail("Problem creating temporary file: " + ioe.getLocalizedMessage()); + } catch (AssertionError ae) { + System.out.println("\nReport:\n"); + System.out.println(report); + throw ae; } } - - @Test - public void runAllJavaPmdOnSourceTree() { - runPmd("-d src/main/java -f text -R rulesets/internal/all-java.xml -language java"); - } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index 6150151d61..3bbf718134 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -44,6 +44,20 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } + /** + * Test ignore identifiers argument with failOnViolation=false with changed long options + */ + @Test + public void testIgnoreIdentifiersFailOnViolationFalseLongOption() throws Exception { + runCPD("--minimum-tokens", "34", "--language", "java", "--files", + "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--fail-on-violation", + "false"); + + String out = getOutput(); + Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication")); + Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); + } + /** * Test excludes option. */ diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaLanguageModuleTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaLanguageModuleTest.java new file mode 100644 index 0000000000..12bb3b0b5c --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaLanguageModuleTest.java @@ -0,0 +1,51 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; + +public class JavaLanguageModuleTest { + private Language javaLanguage = LanguageRegistry.getLanguage(JavaLanguageModule.NAME); + + @Test + public void java9IsSmallerThanJava10() { + LanguageVersion java9 = javaLanguage.getVersion("9"); + LanguageVersion java10 = javaLanguage.getVersion("10"); + + Assert.assertTrue("java9 should be smaller than java10", java9.compareTo(java10) < 0); + } + + @Test + public void previewVersionShouldBeGreaterThanNonPreview() { + LanguageVersion java16 = javaLanguage.getVersion("16"); + LanguageVersion java16p = javaLanguage.getVersion("16-preview"); + + Assert.assertTrue("java16-preview should be greater than java16", java16p.compareTo(java16) > 0); + } + + @Test + public void testCompareToVersion() { + LanguageVersion java9 = javaLanguage.getVersion("9"); + Assert.assertTrue("java9 should be smaller than java10", java9.compareToVersion("10") < 0); + } + + @Test + public void allVersions() { + List versions = javaLanguage.getVersions(); + for (int i = 1; i < versions.size(); i++) { + LanguageVersion previous = versions.get(i - 1); + LanguageVersion current = versions.get(i); + Assert.assertTrue("Version " + previous + " should be smaller than " + current, + previous.compareTo(current) < 0); + } + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15PreviewTreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15PreviewTreeDumpTest.java deleted file mode 100644 index 653ab44e35..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15PreviewTreeDumpTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import java.util.List; - -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; - -import net.sourceforge.pmd.lang.ast.ParseException; -import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; -import net.sourceforge.pmd.lang.java.BaseJavaTreeDumpTest; -import net.sourceforge.pmd.lang.java.JavaParsingHelper; - -public class Java15PreviewTreeDumpTest extends BaseJavaTreeDumpTest { - private final JavaParsingHelper java15p = - JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("15-preview") - .withResourceContext(Java15PreviewTreeDumpTest.class, "jdkversiontests/java15p/"); - private final JavaParsingHelper java15 = java15p.withDefaultVersion("15"); - - - @Override - public BaseParsingHelper getParser() { - return java15p; - } - - @Test - public void patternMatchingInstanceof() { - doTest("PatternMatchingInstanceof"); - - // extended tests for type resolution etc. - ASTCompilationUnit compilationUnit = java15p.parseResource("PatternMatchingInstanceof.java"); - List instanceOfExpressions = compilationUnit.findDescendantsOfType(ASTInstanceOfExpression.class); - for (ASTInstanceOfExpression expr : instanceOfExpressions) { - ASTVariableDeclaratorId variable = expr.getChild(1).getFirstChildOfType(ASTVariableDeclaratorId.class); - Assert.assertEquals(String.class, variable.getType()); - // Note: these variables are not part of the symbol table - // See ScopeAndDeclarationFinder#visit(ASTVariableDeclaratorId, Object) - Assert.assertNull(variable.getNameDeclaration()); - } - } - - @Test(expected = ParseException.class) - public void patternMatchingInstanceofBeforeJava15PreviewShouldFail() { - java15.parseResource("PatternMatchingInstanceof.java"); - } - - @Test - @Ignore("Ignored, this will be reactivated on the typeresolution branch") - public void recordPoint() { - doTest("Point"); - - // extended tests for type resolution etc. - ASTCompilationUnit compilationUnit = java15p.parseResource("Point.java"); - ASTRecordDeclaration recordDecl = compilationUnit.getFirstDescendantOfType(ASTRecordDeclaration.class); - List components = recordDecl.getRecordComponents().toList(); - Assert.assertNull(components.get(0).getVarId().getNameDeclaration().getAccessNodeParent()); - Assert.assertEquals(Integer.TYPE, components.get(0).getVarId().getNameDeclaration().getType()); - Assert.assertEquals("int", components.get(0).getVarId().getNameDeclaration().getTypeImage()); - } - - @Test(expected = ParseException.class) - public void recordPointBeforeJava15PreviewShouldFail() { - java15.parseResource("Point.java"); - } - - @Test(expected = ParseException.class) - public void recordCtorWithThrowsShouldFail() { - java15p.parse(" record R {" - + " R throws IOException {}" - + " }"); - } - - @Test(expected = ParseException.class) - public void recordMustNotExtend() { - java15p.parse("record RecordEx(int x) extends Number { }"); - } - - @Test - public void innerRecords() { - doTest("Records"); - } - - @Test(expected = ParseException.class) - public void recordIsARestrictedIdentifier() { - java15p.parse("public class record {}"); - } - - @Test - public void localRecords() { - doTest("LocalRecords"); - } - - @Test(expected = ParseException.class) - public void sealedClassBeforeJava15Preview() { - java15.parseResource("geometry/Shape.java"); - } - - @Test - public void sealedClass() { - doTest("geometry/Shape"); - } - - @Test - public void nonSealedClass() { - doTest("geometry/Square"); - } - - @Test(expected = ParseException.class) - public void sealedInterfaceBeforeJava15Preview() { - java15.parseResource("expression/Expr.java"); - } - - @Test - public void sealedInterface() { - doTest("expression/Expr"); - } - - @Test - public void localInterfaceAndEnums() { - doTest("LocalInterfacesAndEnums"); - } - - @Test(expected = ParseException.class) - public void localInterfacesAndEnumsBeforeJava15PreviewShouldFail() { - java15.parseResource("LocalInterfacesAndEnums.java"); - } -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15TreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15TreeDumpTest.java index ed888dc1e2..ecb6cdbdb6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15TreeDumpTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java15TreeDumpTest.java @@ -15,7 +15,6 @@ public class Java15TreeDumpTest extends BaseJavaTreeDumpTest { private final JavaParsingHelper java15 = JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("15") .withResourceContext(Java15TreeDumpTest.class, "jdkversiontests/java15/"); - private final JavaParsingHelper java15p = java15.withDefaultVersion("15-preview"); private final JavaParsingHelper java14 = java15.withDefaultVersion("14"); @Override @@ -26,7 +25,6 @@ public class Java15TreeDumpTest extends BaseJavaTreeDumpTest { @Test public void textBlocks() { doTest("TextBlocks"); - java15p.parseResource("TextBlocks.java"); // make sure we can parse it with preview as well } @Test(expected = net.sourceforge.pmd.lang.ast.ParseException.class) @@ -42,6 +40,5 @@ public class Java15TreeDumpTest extends BaseJavaTreeDumpTest { @Test public void sealedAndNonSealedIdentifiers() { doTest("NonSealedIdentifier"); - java15p.parseResource("NonSealedIdentifier.java"); // make sure we can parse it with preview as well } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java index 1be6c39e99..001a187131 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java16TreeDumpTest.java @@ -19,7 +19,7 @@ import net.sourceforge.pmd.lang.java.types.JPrimitiveType; public class Java16TreeDumpTest extends BaseJavaTreeDumpTest { private final JavaParsingHelper java16 = JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("16") - .withResourceContext(Java15TreeDumpTest.class, "jdkversiontests/java16/"); + .withResourceContext(Java16TreeDumpTest.class, "jdkversiontests/java16/"); private final JavaParsingHelper java16p = java16.withDefaultVersion("16-preview"); private final JavaParsingHelper java15 = java16.withDefaultVersion("15"); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java new file mode 100644 index 0000000000..372f271592 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17PreviewTreeDumpTest.java @@ -0,0 +1,92 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; + +import net.sourceforge.pmd.lang.ast.ParseException; +import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; +import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest; +import net.sourceforge.pmd.lang.ast.test.RelevantAttributePrinter; +import net.sourceforge.pmd.lang.java.JavaParsingHelper; + +public class Java17PreviewTreeDumpTest extends BaseTreeDumpTest { + private final JavaParsingHelper java17p = + JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("17-preview") + .withResourceContext(Java17PreviewTreeDumpTest.class, "jdkversiontests/java17p/"); + private final JavaParsingHelper java17 = java17p.withDefaultVersion("17"); + + public Java17PreviewTreeDumpTest() { + super(new RelevantAttributePrinter(), ".java"); + } + + @Override + public BaseParsingHelper getParser() { + return java17p; + } + + @Test + public void patternMatchingForSwitchBeforeJava17Preview() { + ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + java17.parseResource("PatternsInSwitchLabels.java"); + } + }); + Assert.assertTrue("Unexpected message: " + thrown.getMessage(), + thrown.getMessage().contains("Pattern matching for switch is a preview feature of JDK 17, you should select your language version accordingly")); + } + + @Test + public void patternMatchingForSwitch() { + doTest("PatternsInSwitchLabels"); + } + + @Test + public void enhancedTypeCheckingSwitch() { + doTest("EnhancedTypeCheckingSwitch"); + } + + @Test + public void scopeOfPatternVariableDeclarations() { + doTest("ScopeOfPatternVariableDeclarations"); + } + + @Test + public void dealingWithNullBeforeJava17Preview() { + ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + java17.parseResource("DealingWithNull.java"); + } + }); + Assert.assertTrue("Unexpected message: " + thrown.getMessage(), + thrown.getMessage().contains("Null case labels is a preview feature of JDK 17, you should select your language version accordingly")); + } + + @Test + public void dealingWithNull() { + doTest("DealingWithNull"); + } + + @Test + public void guardedAndParenthesizedPatternsBeforeJava17Preview() { + ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + java17.parseResource("GuardedAndParenthesizedPatterns.java"); + } + }); + Assert.assertTrue("Unexpected message: " + thrown.getMessage(), + thrown.getMessage().contains("Guarded patterns is a preview feature of JDK 17, you should select your language version accordingly")); + } + + @Test + public void guardedAndParenthesizedPatterns() { + doTest("GuardedAndParenthesizedPatterns"); + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java new file mode 100644 index 0000000000..5b1b36b4f5 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java17TreeDumpTest.java @@ -0,0 +1,79 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; + +import net.sourceforge.pmd.lang.ast.ParseException; +import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; +import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest; +import net.sourceforge.pmd.lang.ast.test.RelevantAttributePrinter; +import net.sourceforge.pmd.lang.java.JavaParsingHelper; + +public class Java17TreeDumpTest extends BaseTreeDumpTest { + private final JavaParsingHelper java17 = + JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("17") + .withResourceContext(Java17TreeDumpTest.class, "jdkversiontests/java17/"); + private final JavaParsingHelper java17p = java17.withDefaultVersion("17-preview"); + private final JavaParsingHelper java16 = java17.withDefaultVersion("16"); + + public Java17TreeDumpTest() { + super(new RelevantAttributePrinter(), ".java"); + } + + @Override + public BaseParsingHelper getParser() { + return java17; + } + + @Test + public void sealedClassBeforeJava17() { + ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + java16.parseResource("geometry/Shape.java"); + } + }); + Assert.assertTrue("Unexpected message: " + thrown.getMessage(), + thrown.getMessage().contains("Sealed classes is a preview feature of JDK 16, you should select your language version accordingly")); + } + + @Test + public void sealedClass() { + doTest("geometry/Shape"); + java17p.parseResource("geometry/Shape.java"); // make sure we can parse it with preview as well + } + + @Test + public void nonSealedClass() { + doTest("geometry/Square"); + java17p.parseResource("geometry/Square.java"); // make sure we can parse it with preview as well + } + + @Test + public void sealedInterfaceBeforeJava17() { + ParseException thrown = Assert.assertThrows(ParseException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + java16.parseResource("expression/Expr.java"); + } + }); + Assert.assertTrue("Unexpected message: " + thrown.getMessage(), + thrown.getMessage().contains("Sealed classes is a preview feature of JDK 16, you should select your language version accordingly")); + } + + @Test + public void sealedInterface() { + doTest("expression/Expr"); + java17p.parseResource("expression/Expr.java"); // make sure we can parse it with preview as well + } + + @Test + public void localVars() { + doTest("LocalVars"); + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedNameTest.java index 28d69d2ec3..a55fe2d048 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedNameTest.java @@ -24,7 +24,7 @@ public class JavaQualifiedNameTest { private List getNodes(Class target, String code) { - return JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("15-preview").getNodes(target, code); + return JavaParsingHelper.WITH_PROCESSING.withDefaultVersion("15").getNodes(target, code); } @Test diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java index 8d61351586..939ae31248 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java @@ -40,8 +40,8 @@ public class ParserCornersTest extends BaseJavaTreeDumpTest { @Test public void testInvalidUnicodeEscape() { expect.expect(MalformedSourceException.class); // previously Error - expect.expectMessage("Source format error at line 1, column 1: Invalid unicode escape"); - java.parse("\\u00k0"); + expect.expectMessage("Source format error in file x/filename.java at line 1, column 1: Invalid unicode escape"); + java.parse("\\u00k0", null, "x/filename.java"); } /** @@ -204,7 +204,7 @@ public class ParserCornersTest extends BaseJavaTreeDumpTest { public void testGitHubBug2767() { // PMD fails to parse an initializer block. // PMD 6.26.0 parses this code just fine. - java.withDefaultVersion("15-preview") + java.withDefaultVersion("16") .parse("class Foo {\n" + " {final int I;}\n" + "}\n"); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PrimitiveWrapperInstantiationTest.java similarity index 76% rename from pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java rename to pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PrimitiveWrapperInstantiationTest.java index a593a9e0ff..a298bef836 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PrimitiveWrapperInstantiationTest.java @@ -6,6 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -public class UseAssertNullInsteadOfAssertTrueTest extends PmdRuleTst { +public class PrimitiveWrapperInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SimplifiableTestAssertionTest.java similarity index 67% rename from pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java rename to pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SimplifiableTestAssertionTest.java index cb8ded71bf..af8b000034 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SimplifiableTestAssertionTest.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -public class UseAssertTrueInsteadOfAssertEqualsTest extends PmdRuleTst { +@org.junit.Ignore("Rule has not been updated yet") +public class SimplifiableTestAssertionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java deleted file mode 100644 index e874a309b1..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.bestpractices; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -public class UseAssertSameInsteadOfAssertTrueTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/AbstractBuilderMixedTypeVarOverride.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/AbstractBuilderMixedTypeVarOverride.java new file mode 100644 index 0000000000..322e677c78 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/AbstractBuilderMixedTypeVarOverride.java @@ -0,0 +1,31 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride; + +import java.util.Collection; + +public abstract class AbstractBuilderMixedTypeVarOverride, T> { + @SuppressWarnings("unchecked") + public B defaultValue(T val) { + return (B) this; + } + + @SuppressWarnings("unchecked") + public B defaultValue2(T val) { + return (B) this; + } + + public static final class ConcreteBuilder> extends AbstractBuilderMixedTypeVarOverride, C> { + //@Override is wrong here: method does not override or implement a method from a supertype + public ConcreteBuilder defaultValue(Collection val) { + return this; + } + + @Override + public ConcreteBuilder defaultValue2(C val) { + return this; + } + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/EnumToString.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/EnumToString.java new file mode 100644 index 0000000000..ab3f6d0d9b --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/EnumToString.java @@ -0,0 +1,28 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride; + +public enum EnumToString { + sub_EnumClazz { + // missing @Override + public String toString() { + return "test"; + } + + // missing @Override + public void notOverride() { + System.out.println("test"); + } + }; + + // missing @Override + public String toString() { + return "test"; + } + + public void notOverride() { + System.out.println("test"); + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/EnumWithAnonClass.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/EnumWithAnonClass.java index 62268dc324..04b00abecd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/EnumWithAnonClass.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/EnumWithAnonClass.java @@ -10,15 +10,18 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride; */ public enum EnumWithAnonClass { Foo { - @Override + // missing + public String toString() { + return super.toString(); + } + + // missing public String getSomething() { return null; } }; - public Object getSomething() { return null; } - } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryBoxingTest.java similarity index 79% rename from pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java rename to pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryBoxingTest.java index 19c6c714fd..140e985a35 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryBoxingTest.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -6,6 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -public class DefaultPackageTest extends PmdRuleTst { +public class UnnecessaryBoxingTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/AvoidUncheckedExceptionsInSignaturesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/AvoidUncheckedExceptionsInSignaturesTest.java index 9e2cc6b138..62c9c4e7e8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/AvoidUncheckedExceptionsInSignaturesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/AvoidUncheckedExceptionsInSignaturesTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidUncheckedExceptionsInSignaturesTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/ClassWithOnlyPrivateConstructorsShouldBeFinalTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/ClassWithOnlyPrivateConstructorsShouldBeFinalTest.java index 929c188965..1687261131 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/ClassWithOnlyPrivateConstructorsShouldBeFinalTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/ClassWithOnlyPrivateConstructorsShouldBeFinalTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ClassWithOnlyPrivateConstructorsShouldBeFinalTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/CollapsibleIfStatementsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/CollapsibleIfStatementsTest.java index 35c4af18fd..f348216869 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/CollapsibleIfStatementsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/CollapsibleIfStatementsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CollapsibleIfStatementsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/FinalFieldCouldBeStaticTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/FinalFieldCouldBeStaticTest.java index 48eb20d000..1b2bf96d7c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/FinalFieldCouldBeStaticTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/FinalFieldCouldBeStaticTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class FinalFieldCouldBeStaticTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/LogicInversionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/LogicInversionTest.java index 1dc741dcc9..38959833e5 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/LogicInversionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/LogicInversionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LogicInversionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifiedTernaryTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifiedTernaryTest.java index b57ae4b5b8..c381684027 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifiedTernaryTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifiedTernaryTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SimplifiedTernaryTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanAssertionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanAssertionTest.java deleted file mode 100644 index 365bb56a0d..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanAssertionTest.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.design; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -@org.junit.Ignore("Rule has not been updated yet") -public class SimplifyBooleanAssertionTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyConditionalTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyConditionalTest.java index 43ed3e5754..927ae8d8f8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyConditionalTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyConditionalTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SimplifyConditionalTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityTest.java index c19a339f55..0da92cecac 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SwitchDensityTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/UseUtilityClassTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/UseUtilityClassTest.java index b9275c8101..36e7942a09 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/UseUtilityClassTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/UseUtilityClassTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseUtilityClassTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodTest.java index 2dae318180..47bc719839 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UselessOverridingMethodTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/BaseClass.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/BaseClass.java index 61c3799caa..586e0b917f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/BaseClass.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/BaseClass.java @@ -6,11 +6,15 @@ package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod; public class BaseClass { - protected void doBase() { } - protected void doBaseWithArg(String foo) { } + + protected void doBaseWithArgs(String foo, int bar) { + } + + protected void methodWithInterface(CharSequence arg) { + } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/DirectSubclass.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/DirectSubclass.java index ed73905e7b..86d58db018 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/DirectSubclass.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/DirectSubclass.java @@ -6,15 +6,20 @@ package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod; public class DirectSubclass extends BaseClass { + // overrides to make the methods public @Override public void doBase() { super.doBase(); } - @Override public void doBaseWithArg(String foo) { super.doBaseWithArg(foo); } + + @Override + public void doBaseWithArgs(String foo, int bar) { + super.doBaseWithArgs(foo, bar); + } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/DirectSubclass2.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/DirectSubclass2.java index 4a282aeba7..250ae61edc 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/DirectSubclass2.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/DirectSubclass2.java @@ -5,8 +5,19 @@ package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod; public class DirectSubclass2 extends DirectSubclass { + // useless overrides - it's already public @Override public void doBase() { super.doBase(); } + + @Override + public void doBaseWithArg(String foo) { + super.doBaseWithArg(foo); + } + + @Override + public void doBaseWithArgs(String foo, int bar) { + super.doBaseWithArgs(foo, bar); + } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/GeneratedValue.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/GeneratedValue.java new file mode 100644 index 0000000000..1b5fbabfb2 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/GeneratedValue.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod; + +public @interface GeneratedValue { + GenerationType strategy() default GenerationType.AUTO; +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/GenerationType.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/GenerationType.java new file mode 100644 index 0000000000..43d999e1a2 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/GenerationType.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod; + +public enum GenerationType { + AUTO +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/Id.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/Id.java new file mode 100644 index 0000000000..a3b5066638 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/Id.java @@ -0,0 +1,8 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod; + +public @interface Id { +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/OtherSubclass.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/OtherSubclass.java index 4f8c9d23c6..1469ba18a5 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/OtherSubclass.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/OtherSubclass.java @@ -5,4 +5,9 @@ package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod; public class OtherSubclass extends BaseClass { + + // note: argument type overloaded from CharSequence to String + protected void methodWithInterface(String arg) { + super.methodWithInterface(arg); + } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/TransitiveSubclass.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/TransitiveSubclass.java index 050665ec8e..63ed6d4c5d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/TransitiveSubclass.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/TransitiveSubclass.java @@ -6,8 +6,20 @@ package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod; public class TransitiveSubclass extends OtherSubclass { + // overrides to make the methods public + @Override public void doBase() { super.doBase(); } + + @Override + public void doBaseWithArg(String foo) { + super.doBaseWithArg(foo); + } + + @Override + public void doBaseWithArgs(String foo, int bar) { + super.doBaseWithArgs(foo, bar); + } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/other/DirectSubclassInOtherPackage.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/other/DirectSubclassInOtherPackage.java index 0585d7e4dd..d08190d2ef 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/other/DirectSubclassInOtherPackage.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/other/DirectSubclassInOtherPackage.java @@ -8,8 +8,20 @@ import net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod.BaseCla public class DirectSubclassInOtherPackage extends BaseClass { + // overrides to make the method available in this package + @Override protected void doBase() { super.doBase(); } + + @Override + protected void doBaseWithArg(String foo) { + super.doBaseWithArg(foo); + } + + @Override + protected void doBaseWithArgs(String foo, int bar) { + super.doBaseWithArgs(foo, bar); + } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/other/OtherClassInOtherPackage.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/other/OtherClassInOtherPackage.java index 80ec2a66d9..29d4d8d773 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/other/OtherClassInOtherPackage.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/uselessoverridingmethod/other/OtherClassInOtherPackage.java @@ -6,11 +6,12 @@ package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod.other; public class OtherClassInOtherPackage { - public void foo() { DirectSubclassInOtherPackage instance = new DirectSubclassInOtherPackage(); - // this call is only possible, because DirectSubclassInOtherPackage makes this + // the following calls are only possible, because DirectSubclassInOtherPackage makes this // method available in this package as well. instance.doBase(); + instance.doBaseWithArg("a"); + instance.doBaseWithArgs("a", 1); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticTest.java index 34dc36708e..3c1ee83e89 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AssignmentToNonFinalStaticTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneThrowsCloneNotSupportedExceptionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAccessibilityAlterationTest.java similarity index 63% rename from pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneThrowsCloneNotSupportedExceptionTest.java rename to pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAccessibilityAlterationTest.java index 2dfbc243d2..cd5c41a995 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneThrowsCloneNotSupportedExceptionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAccessibilityAlterationTest.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") -public class CloneThrowsCloneNotSupportedExceptionTest extends PmdRuleTst { +public class AvoidAccessibilityAlterationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidBranchingStatementAsLastInLoopTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidBranchingStatementAsLastInLoopTest.java index 3a266f5964..3628a5de05 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidBranchingStatementAsLastInLoopTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidBranchingStatementAsLastInLoopTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidBranchingStatementAsLastInLoopTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidInstanceofChecksInCatchClauseTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidInstanceofChecksInCatchClauseTest.java index 7ed53244d5..6742ca059f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidInstanceofChecksInCatchClauseTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidInstanceofChecksInCatchClauseTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidInstanceofChecksInCatchClauseTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidUsingOctalValuesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidUsingOctalValuesTest.java index 5dc1362bce..cf4f8736bd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidUsingOctalValuesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidUsingOctalValuesTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidUsingOctalValuesTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CheckSkipResultTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CheckSkipResultTest.java index e096adbab3..03ff885411 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CheckSkipResultTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CheckSkipResultTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CheckSkipResultTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ClassCastExceptionWithToArrayTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ClassCastExceptionWithToArrayTest.java index 35615ade71..cbc7d41463 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ClassCastExceptionWithToArrayTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ClassCastExceptionWithToArrayTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ClassCastExceptionWithToArrayTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustBePublicTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustBePublicTest.java index 1fe93eb217..3a929d920e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustBePublicTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustBePublicTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CloneMethodMustBePublicTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodReturnTypeMustMatchClassNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodReturnTypeMustMatchClassNameTest.java index fee63a7fba..3ce9336297 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodReturnTypeMustMatchClassNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodReturnTypeMustMatchClassNameTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CloneMethodReturnTypeMustMatchClassNameTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceTest.java index 2b58a9fdec..a20781ae9c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CloseResourceTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ComparisonWithNaNTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ComparisonWithNaNTest.java index 6bdd3704fd..bb84ab84e1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ComparisonWithNaNTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ComparisonWithNaNTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ComparisonWithNaNTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DoNotCallGarbageCollectionExplicitlyTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DoNotCallGarbageCollectionExplicitlyTest.java index 0958db4ea1..3d3b63a54a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DoNotCallGarbageCollectionExplicitlyTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DoNotCallGarbageCollectionExplicitlyTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DoNotCallGarbageCollectionExplicitlyTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontImportSunTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontImportSunTest.java index 397c327e83..b0951490c6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontImportSunTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontImportSunTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DontImportSunTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontUseFloatTypeForLoopIndicesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontUseFloatTypeForLoopIndicesTest.java index 4197462906..bff2a2c3f3 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontUseFloatTypeForLoopIndicesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/DontUseFloatTypeForLoopIndicesTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DontUseFloatTypeForLoopIndicesTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeDoesNotCallSuperFinalizeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeDoesNotCallSuperFinalizeTest.java index f74194a4d5..97017d2b87 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeDoesNotCallSuperFinalizeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeDoesNotCallSuperFinalizeTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class FinalizeDoesNotCallSuperFinalizeTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeOnlyCallsSuperFinalizeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeOnlyCallsSuperFinalizeTest.java index 929b7ebe7b..af0ab11739 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeOnlyCallsSuperFinalizeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeOnlyCallsSuperFinalizeTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class FinalizeOnlyCallsSuperFinalizeTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeOverloadedTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeOverloadedTest.java index 5efb414193..e123e98a1c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeOverloadedTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeOverloadedTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class FinalizeOverloadedTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeShouldBeProtectedTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeShouldBeProtectedTest.java index 5fb2be61f2..73b0ee58fc 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeShouldBeProtectedTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/FinalizeShouldBeProtectedTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class FinalizeShouldBeProtectedTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/IdempotentOperationsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/IdempotentOperationsTest.java index 753a5ce1cd..6b29749f2f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/IdempotentOperationsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/IdempotentOperationsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class IdempotentOperationsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingBreakInSwitchTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughTest.java similarity index 78% rename from pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingBreakInSwitchTest.java rename to pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughTest.java index c78f69909e..f7bf9c8ab3 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingBreakInSwitchTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ImplicitSwitchFallThroughTest.java @@ -6,6 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -public class MissingBreakInSwitchTest extends PmdRuleTst { +public class ImplicitSwitchFallThroughTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/InstantiationToGetClassTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/InstantiationToGetClassTest.java index 002a4b5e05..f27f820f6f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/InstantiationToGetClassTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/InstantiationToGetClassTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class InstantiationToGetClassTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatTest.java index 6afd6c19df..d0fed80464 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class InvalidLogMessageFormatTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JumbledIncrementerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JumbledIncrementerTest.java index 178d0eac7e..90f349f243 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JumbledIncrementerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JumbledIncrementerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JumbledIncrementerTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MisplacedNullCheckTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MisplacedNullCheckTest.java index 1ef80cb074..371de4f655 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MisplacedNullCheckTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MisplacedNullCheckTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class MisplacedNullCheckTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingSerialVersionUIDTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingSerialVersionUIDTest.java index 608f84996c..b2437fbaf8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingSerialVersionUIDTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingSerialVersionUIDTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class MissingSerialVersionUIDTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingStaticMethodInNonInstantiatableClassTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingStaticMethodInNonInstantiatableClassTest.java index b0b2de8231..f3978f3429 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingStaticMethodInNonInstantiatableClassTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/MissingStaticMethodInNonInstantiatableClassTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class MissingStaticMethodInNonInstantiatableClassTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/NonCaseLabelInSwitchStatementTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/NonCaseLabelInSwitchStatementTest.java index 255ac134a4..ab4a88a5d8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/NonCaseLabelInSwitchStatementTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/NonCaseLabelInSwitchStatementTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class NonCaseLabelInSwitchStatementTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/NonStaticInitializerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/NonStaticInitializerTest.java index 8f3c4fa311..f009ce5ab2 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/NonStaticInitializerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/NonStaticInitializerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class NonStaticInitializerTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java index d371d5e280..32d88b23b1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class OverrideBothEqualsAndHashcodeTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationTest.java index 3866de86cf..dc40b33138 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperCloneImplementationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ProperCloneImplementationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperLoggerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperLoggerTest.java index b066c30625..18f13af50b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperLoggerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ProperLoggerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ProperLoggerTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnEmptyArrayRatherThanNullTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnEmptyCollectionRatherThanNullTest.java similarity index 66% rename from pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnEmptyArrayRatherThanNullTest.java rename to pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnEmptyCollectionRatherThanNullTest.java index 428d6737bf..3dde294114 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnEmptyArrayRatherThanNullTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnEmptyCollectionRatherThanNullTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") -public class ReturnEmptyArrayRatherThanNullTest extends PmdRuleTst { +public class ReturnEmptyCollectionRatherThanNullTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnFromFinallyBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnFromFinallyBlockTest.java index 1d83115c71..5945a9133f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnFromFinallyBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/ReturnFromFinallyBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ReturnFromFinallyBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SimpleDateFormatNeedsLocaleTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SimpleDateFormatNeedsLocaleTest.java index d128604254..6a8c7b0c7e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SimpleDateFormatNeedsLocaleTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SimpleDateFormatNeedsLocaleTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SimpleDateFormatNeedsLocaleTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingleMethodSingletonTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingleMethodSingletonTest.java index 1898453d8f..067a517f94 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingleMethodSingletonTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingleMethodSingletonTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SingleMethodSingletonTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingletonClassReturningNewInstanceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingletonClassReturningNewInstanceTest.java index 7730485458..ca957ed18f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingletonClassReturningNewInstanceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SingletonClassReturningNewInstanceTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SingletonClassReturningNewInstanceTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/StaticEJBFieldShouldBeFinalTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/StaticEJBFieldShouldBeFinalTest.java index c4dbd6e871..af2f8fcdf2 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/StaticEJBFieldShouldBeFinalTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/StaticEJBFieldShouldBeFinalTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class StaticEJBFieldShouldBeFinalTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/StringBufferInstantiationWithCharTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/StringBufferInstantiationWithCharTest.java index d694ae1e94..f4e819b328 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/StringBufferInstantiationWithCharTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/StringBufferInstantiationWithCharTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class StringBufferInstantiationWithCharTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousEqualsMethodNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousEqualsMethodNameTest.java index 81ffd29e6c..7787ade01b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousEqualsMethodNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousEqualsMethodNameTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SuspiciousEqualsMethodNameTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousHashcodeMethodNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousHashcodeMethodNameTest.java index 87486c25a2..28d3edba4e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousHashcodeMethodNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousHashcodeMethodNameTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SuspiciousHashcodeMethodNameTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousOctalEscapeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousOctalEscapeTest.java index 8d15360ef1..c5549023aa 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousOctalEscapeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/SuspiciousOctalEscapeTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SuspiciousOctalEscapeTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnconditionalIfStatementTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnconditionalIfStatementTest.java index e82749bec2..6af4a7f8e3 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnconditionalIfStatementTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnconditionalIfStatementTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UnconditionalIfStatementTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryBooleanAssertionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryBooleanAssertionTest.java index dd6c3ad095..2b343d5134 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryBooleanAssertionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryBooleanAssertionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UnnecessaryBooleanAssertionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryCaseChangeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryCaseChangeTest.java index 5bd4304f20..4cbb6ea7e3 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryCaseChangeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryCaseChangeTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UnnecessaryCaseChangeTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryConversionTemporaryTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryConversionTemporaryTest.java index 8fa217becd..aa375ea104 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryConversionTemporaryTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnnecessaryConversionTemporaryTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UnnecessaryConversionTemporaryTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnusedNullCheckInEqualsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnusedNullCheckInEqualsTest.java index b2ae0f4450..0d5020056d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnusedNullCheckInEqualsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UnusedNullCheckInEqualsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UnusedNullCheckInEqualsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseCorrectExceptionLoggingTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseCorrectExceptionLoggingTest.java index b7d944346a..f6efc45715 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseCorrectExceptionLoggingTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseCorrectExceptionLoggingTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseCorrectExceptionLoggingTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseEqualsToCompareStringsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseEqualsToCompareStringsTest.java index 2ac4f0f2dc..903c0d420d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseEqualsToCompareStringsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseEqualsToCompareStringsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseEqualsToCompareStringsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseLocaleWithCaseConversionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseLocaleWithCaseConversionsTest.java index d4b26e450b..7c0403ba8d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseLocaleWithCaseConversionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseLocaleWithCaseConversionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseLocaleWithCaseConversionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseProperClassLoaderTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseProperClassLoaderTest.java index cf00f3869d..ede7271289 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseProperClassLoaderTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UseProperClassLoaderTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseProperClassLoaderTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/DAOTransaction.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/DAOTransaction.java new file mode 100644 index 0000000000..8cf0d8a0be --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/DAOTransaction.java @@ -0,0 +1,10 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.closeresource; + +public interface DAOTransaction { + + void commit(); +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/MyClass.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/MyClass.java new file mode 100644 index 0000000000..ec1b31789a --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/MyClass.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.closeresource; + +import java.sql.PreparedStatement; + +public class MyClass { + + public void cleanup() { } + + public void applyTransactionTimeout(PreparedStatement stmt) { } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/MyHelper.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/MyHelper.java new file mode 100644 index 0000000000..021d6a7739 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/MyHelper.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.closeresource; + +public final class MyHelper { + + private MyHelper() { } + + public static void close(Statement s) { } + + public static void myClose(Statement s) { } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/Pool.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/Pool.java new file mode 100644 index 0000000000..1c24d57102 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/Pool.java @@ -0,0 +1,18 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.closeresource; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; + +public interface Pool { + + Connection getConnection(); + + Statement getStmt(); + + ResultSet getRS(); +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/TransactionManager.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/TransactionManager.java new file mode 100644 index 0000000000..ce2c78b2a4 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/closeresource/TransactionManager.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.closeresource; + +public interface TransactionManager { + DAOTransaction open(); +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/NonThreadSafeSingletonTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/NonThreadSafeSingletonTest.java index a7103126c4..928f350179 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/NonThreadSafeSingletonTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/NonThreadSafeSingletonTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class NonThreadSafeSingletonTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UnsynchronizedStaticFormatterTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UnsynchronizedStaticFormatterTest.java index 37f12dc791..f5d0288dfe 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UnsynchronizedStaticFormatterTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UnsynchronizedStaticFormatterTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UnsynchronizedStaticFormatterTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidArrayLoopsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidArrayLoopsTest.java index 4f64ee0192..6377d8f891 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidArrayLoopsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidArrayLoopsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidArrayLoopsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidCalendarDateCreationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidCalendarDateCreationTest.java index fb5991354b..a3c8909f50 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidCalendarDateCreationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidCalendarDateCreationTest.java @@ -6,6 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidCalendarDateCreationTest extends PmdRuleTst { + // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsTest.java index 6ec1634486..33cea34ab7 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidInstantiatingObjectsInLoopsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidInstantiatingObjectsInLoopsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java deleted file mode 100644 index 5a639a8a74..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -public class AvoidUsingShortTypeTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/BigIntegerInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/BigIntegerInstantiationTest.java index c24a05c4c6..2903a45822 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/BigIntegerInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/BigIntegerInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class BigIntegerInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/BooleanInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/BooleanInstantiationTest.java deleted file mode 100644 index 57510e54c8..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/BooleanInstantiationTest.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -@org.junit.Ignore("Rule has not been updated yet") -public class BooleanInstantiationTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java deleted file mode 100644 index 2db2199779..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -public class ByteInstantiationTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java deleted file mode 100644 index a951c8eda7..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -public class IntegerInstantiationTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java deleted file mode 100644 index a2b951cb00..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -public class LongInstantiationTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java deleted file mode 100644 index 564c94b6fd..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -public class ShortInstantiationTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java deleted file mode 100644 index e2a3fbcd15..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -public class SimplifyStartsWithTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UnnecessaryWrapperObjectCreationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UnnecessaryWrapperObjectCreationTest.java deleted file mode 100644 index 8994e983f2..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UnnecessaryWrapperObjectCreationTest.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.testframework.PmdRuleTst; - -@org.junit.Ignore("Rule has not been updated yet") -public class UnnecessaryWrapperObjectCreationTest extends PmdRuleTst { - // no additional unit tests -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/GenericMethodReferenceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/GenericMethodReferenceTest.java new file mode 100644 index 0000000000..bbf100514c --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/GenericMethodReferenceTest.java @@ -0,0 +1,32 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.types; + +import java.util.function.Supplier; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.java.JavaParsingHelper; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; +import net.sourceforge.pmd.lang.java.types.testdata.GenericMethodReference; + +public class GenericMethodReferenceTest { + + @Test + public void typeResolveVariable() { + ASTCompilationUnit root = JavaParsingHelper.WITH_PROCESSING.parseClass(GenericMethodReference.class); + + root.descendants(ASTVariableDeclaratorId.class).forEach(variable -> { + Assert.assertTrue(variable.getName().startsWith("supplier")); + @Nullable + JTypeDeclSymbol symbol = variable.getInitializer().getTypeMirror().getSymbol(); + Assert.assertEquals(Supplier.class.getSimpleName(), symbol.getSimpleName()); + }); + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/testdata/GenericMethodReference.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/testdata/GenericMethodReference.java new file mode 100644 index 0000000000..15c43e2892 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/testdata/GenericMethodReference.java @@ -0,0 +1,36 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.types.testdata; + +import java.util.function.Supplier; + +public final class GenericMethodReference { + { + // the constructor is not generic, so is unused, but that should just be ignored + Supplier supplier1 = GenericMethodReference::new; + Supplier supplier2 = GenericMethodReference::new; + + // the create1 method is not generic - any provided types should be ignored + Supplier supplier3 = GenericMethodReference::create1; + Supplier supplier4 = GenericMethodReference::create1; + + // the create2 method is generic, but takes only one parameter + Supplier supplier5 = GenericMethodReference::create2; + + // providing too many parameter here is a compile error + //Supplier supplier6 = GenericMethodReference::create2; + } + + public static GenericMethodReference create1() { + return null; + } + + public static GenericMethodReference create2() { + return null; + } + + private GenericMethodReference() { + } +} diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt index e829bbdc1e..c2829ae510 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLiteralTest.kt @@ -46,7 +46,7 @@ class ASTLiteralTest : ParserTestSpec({ } } - parserTest("Text block literal", javaVersions = since(J15__PREVIEW)) { + parserTest("Text block literal", javaVersions = since(J15)) { val delim = "\"\"\"" diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTPatternTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTPatternTest.kt index 8631e1751f..91539a2238 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTPatternTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTPatternTest.kt @@ -12,9 +12,9 @@ import java.io.IOException class ASTPatternTest : ProcessorTestSpec({ - val typePatternsVersions = JavaVersion.since(J16).plus(J15__PREVIEW) + val typePatternsVersions = JavaVersion.since(J16) - parserTest("Test patterns only available on JDK 15 (preview) and JDK16 and JDK16 (preview)", + parserTest("Test patterns only available on JDK16 and JDK16 (preview) and JDK17 and JDK 17 (preview)", javaVersions = JavaVersion.except(typePatternsVersions)) { inContext(ExpressionParsingCtx) { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt index 0dae3d259f..a7f77a33b7 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt @@ -34,8 +34,9 @@ enum class JavaVersion : Comparable { J12, J13, J14, - J15, J15__PREVIEW, - J16, J16__PREVIEW; + J15, + J16, J16__PREVIEW, + J17, J17__PREVIEW; /** Name suitable for use with e.g. [JavaParsingHelper.parse] */ val pmdName: String = name.removePrefix("J").replaceFirst("__", "-").replace('_', '.').toLowerCase() diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/NodeParsingCtx.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/NodeParsingCtx.kt index adbecd7183..0d16a7762c 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/NodeParsingCtx.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/NodeParsingCtx.kt @@ -90,7 +90,7 @@ object ExpressionParsingCtx : NodeParsingCtx("expression") { object StatementParsingCtx : NodeParsingCtx("statement") { override fun getTemplate(construct: String, ctx: ParserTestCtx): String = - TypeBodyParsingCtx.getTemplate("{\n$construct}", ctx) + TypeBodyParsingCtx.getTemplate(" {\n $construct\n }", ctx) override fun retrieveNode(acu: ASTCompilationUnit): ASTStatement = diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TypeDisambiguationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TypeDisambiguationTest.kt index 304b9fbbf2..3110b12be9 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TypeDisambiguationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TypeDisambiguationTest.kt @@ -124,8 +124,10 @@ class TypeDisambiguationTest : ParserTestSpec({ } """) - val (refInFoo, refInScratch) = acu.descendants(ASTFieldDeclaration::class.java).map { it.typeNode as ASTClassOrInterfaceType }.toList() - val (_, _, aMem) = acu.descendants(ASTClassOrInterfaceDeclaration::class.java).toList { it.symbol } + val (refInFoo, refInScratch) = acu.descendants(ASTFieldDeclaration::class.java) + .crossFindBoundaries().map { it.typeNode as ASTClassOrInterfaceType }.toList() + val (_, _, aMem) = acu.descendants(ASTClassOrInterfaceDeclaration::class.java) + .crossFindBoundaries().toList { it.symbol } doTest("Ambiguous inner type should produce an error (ref in Foo)") { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/AstSymbolTests.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/AstSymbolTests.kt index cab484e6f8..8c67401bb7 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/AstSymbolTests.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/AstSymbolTests.kt @@ -55,7 +55,10 @@ class AstSymbolTests : ParserTestSpec({ @interface Annot { } """) - val (fooClass, innerItf, innerEnum, innerClass, annot) = acu.descendants(ASTAnyTypeDeclaration::class.java).toList { it.symbol } + val (fooClass, innerItf, innerEnum, innerClass, annot) = acu + .descendants(ASTAnyTypeDeclaration::class.java) + .crossFindBoundaries() + .toList { it.symbol } val (barM, ohioM) = acu.descendants(ASTMethodDeclaration::class.java).toList { it.symbol } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/LocalTypeScopesTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/LocalTypeScopesTest.kt index ad36085575..834ebbf720 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/LocalTypeScopesTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/LocalTypeScopesTest.kt @@ -41,8 +41,8 @@ class LocalTypeScopesTest : ParserTestSpec({ val (foo, inner, other) = acu.descendants(ASTAnyTypeDeclaration::class.java).toList { it.typeMirror } - val (insideFoo, insideInner, insideOther) = - acu.descendants(ASTFieldDeclaration::class.java).toList() + val (insideFoo, _, insideOther) = + acu.descendants(ASTFieldDeclaration::class.java).crossFindBoundaries().toList() doTest("Inside a type: other toplevel types and inner classes are in scope") { @@ -119,7 +119,7 @@ class LocalTypeScopesTest : ParserTestSpec({ acu.descendants(ASTClassOrInterfaceDeclaration::class.java).toList() val (insideFoo, insideInner, insideOther) = - acu.descendants(ASTFieldDeclaration::class.java).toList() + acu.descendants(ASTFieldDeclaration::class.java).crossFindBoundaries().toList() doTest("Inside Foo/Inner: Inner is the inner class") { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/MemberInheritanceTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/MemberInheritanceTest.kt index 1ad7057d21..090ff282b0 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/MemberInheritanceTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/MemberInheritanceTest.kt @@ -101,7 +101,10 @@ class MemberInheritanceTest : ParserTestSpec({ """) val (supF, supG, supK, sup2F, outerF, outerG, outerK, innerF) = - acu.descendants(ASTMethodDeclaration::class.java).toList { it.genericSignature } + acu + .descendants(ASTMethodDeclaration::class.java) + .crossFindBoundaries() + .toList { it.genericSignature } val (sup, _, outer, inner) = acu.descendants(ASTAnyTypeDeclaration::class.java).toList { it.body!! } @@ -161,7 +164,10 @@ class MemberInheritanceTest : ParserTestSpec({ """) val (outerF, staticOuter, innerF) = - acu.descendants(ASTMethodDeclaration::class.java).toList { it.genericSignature } + acu + .descendants(ASTMethodDeclaration::class.java) + .crossFindBoundaries() + .toList { it.genericSignature } val (outer, inner) = acu.descendants(ASTAnyTypeDeclaration::class.java).toList { it.body!! } @@ -195,7 +201,9 @@ class MemberInheritanceTest : ParserTestSpec({ """) val (outerF, staticOuter, innerF) = - acu.descendants(ASTMethodDeclaration::class.java).toList { it.genericSignature } + acu.descendants(ASTMethodDeclaration::class.java) + .crossFindBoundaries() + .toList { it.genericSignature } val (outer, inner) = acu.descendants(ASTAnyTypeDeclaration::class.java).toList { it.body!! } @@ -260,7 +268,8 @@ class MemberInheritanceTest : ParserTestSpec({ acu.descendants(ASTClassOrInterfaceDeclaration::class.java).toList { it.typeMirror } val insideFoo = - acu.descendants(ASTClassOrInterfaceBody::class.java).get(2)!! + acu.descendants(ASTClassOrInterfaceBody::class.java) + .crossFindBoundaries().get(2)!! val `t_Scratch{String}Inner` = with (acu.typeDsl) { t_Scratch[gen.t_String].selectInner(t_Inner.symbol, emptyList()) @@ -745,10 +754,12 @@ class Top { """) val importedFieldAccess = acu.descendants(ASTVariableAccess::class.java).firstOrThrow() - val importedFieldSym = acu.descendants(ASTVariableDeclaratorId::class.java).firstOrThrow().symbol + val importedFieldSym = acu.descendants(ASTVariableDeclaratorId::class.java) + .crossFindBoundaries().firstOrThrow().symbol val importedMethodCall = acu.descendants(ASTMethodCall::class.java).firstOrThrow() - val importedMethodSym = acu.descendants(ASTMethodDeclaration::class.java).firstOrThrow().symbol + val importedMethodSym = acu.descendants(ASTMethodDeclaration::class.java) + .crossFindBoundaries().firstOrThrow().symbol importedFieldAccess.referencedSym shouldBe importedFieldSym importedMethodCall.methodType.symbol shouldBe importedMethodSym diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/TypeParamScopingTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/TypeParamScopingTest.kt index 0b7e39dc9f..1e38b89045 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/TypeParamScopingTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/TypeParamScopingTest.kt @@ -43,10 +43,12 @@ class TypeParamScopingTest : ParserTestSpec({ """) val (fooT, inner2T) = - acu.descendants(ASTTypeParameter::class.java).toList() + acu.descendants(ASTTypeParameter::class.java) + .crossFindBoundaries().toList() val (insideFoo, insideInner, insideInner2, insideOther) = - acu.descendants(ASTFieldDeclaration::class.java).toList() + acu.descendants(ASTFieldDeclaration::class.java) + .crossFindBoundaries().toList() doTest("Inside Foo: T is Foo#T") { @@ -225,7 +227,8 @@ class TypeParamScopingTest : ParserTestSpec({ val (tparam) = fooClass.symbol.typeParameters val (insideFoo, insideT) = - acu.descendants(ASTFormalParameter::class.java).toList() + acu.descendants(ASTFormalParameter::class.java) + .crossFindBoundaries().toList() doTest("Inside Foo: T is Foo#T") { insideFoo.symbolTable.shouldResolveTypeTo("T", tparam) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/VarScopingTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/VarScopingTest.kt index 34eebfa614..a51837d87f 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/VarScopingTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/VarScopingTest.kt @@ -61,10 +61,12 @@ class VarScopingTest : ProcessorTestSpec({ acu.descendants(ASTClassOrInterfaceDeclaration::class.java).toList() val (outerField, localInInit, foreachParam, methodParam, localInBlock, innerField) = - acu.descendants(ASTVariableDeclaratorId::class.java).toList() + acu.descendants(ASTVariableDeclaratorId::class.java) + .crossFindBoundaries().toList() val (inInitializer, inForeachInit, inForeach, inMethod, inLocalBlock, inInnerClass) = - acu.descendants(ASTMethodCall::class.java).toList() + acu.descendants(ASTMethodCall::class.java) + .crossFindBoundaries().toList() doTest("Inside outer initializer: f is outerField") { @@ -341,7 +343,8 @@ class VarScopingTest : ProcessorTestSpec({ val (_, t_SomeEnum) = acu.descendants(ASTAnyTypeDeclaration::class.java).toList { it.typeMirror } val (enumA, enumB) = - acu.descendants(ASTVariableDeclaratorId::class.java).toList() + acu.descendants(ASTEnumDeclaration::class.java) + .descendants(ASTVariableDeclaratorId::class.java).toList() val (e, caseA, caseB) = acu.descendants(ASTVariableAccess::class.java).toList() diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeTestMockingUtil.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeTestMockingUtil.kt index 3964b53cb0..b55b19faaf 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeTestMockingUtil.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeTestMockingUtil.kt @@ -93,7 +93,7 @@ fun JavaNode.firstMethodCall() = methodCalls().crossFindBoundaries().firstOrThro fun JavaNode.ctorCalls(): DescendantNodeStream = descendants(ASTConstructorCall::class.java) fun JavaNode.firstCtorCall() = ctorCalls().crossFindBoundaries().firstOrThrow() -fun JavaNode.typeVariables(): MutableList = descendants(ASTTypeParameter::class.java).toList { it.typeMirror } +fun JavaNode.typeVariables(): MutableList = descendants(ASTTypeParameter::class.java).crossFindBoundaries().toList { it.typeMirror } fun JavaNode.varAccesses(name: String): NodeStream = descendants(ASTVariableAccess::class.java).filter { it.name == name } fun JavaNode.varId(name: String) = descendants(ASTVariableDeclaratorId::class.java).filter { it.name == name }.firstOrThrow() fun JavaNode.typeVar(name: String) = descendants(ASTTypeParameter::class.java).filter { it.name == name }.firstOrThrow().typeMirror diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/MethodRefInferenceTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/MethodRefInferenceTest.kt index 02aabff852..10887a01f3 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/MethodRefInferenceTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/MethodRefInferenceTest.kt @@ -548,7 +548,7 @@ class Scratch { """.trimIndent()) val (_, t_Sink) = acu.descendants(ASTClassOrInterfaceDeclaration::class.java).toList { it.typeMirror } - val (_, acceptInt, acceptLong) = acu.descendants(ASTMethodDeclaration::class.java).toList() + val (_, acceptInt, acceptLong) = acu.descendants(ASTMethodDeclaration::class.java).crossFindBoundaries().toList() val (castRef, returnRef) = acu.descendants(ASTMethodReference::class.java).toList() doTest("In cast context") { @@ -936,7 +936,7 @@ class Scratch { """.trimIndent()) val (_, t_NodeStream) = acu.descendants(ASTClassOrInterfaceDeclaration::class.java).toList { it.typeMirror } - val (_, tvar) = acu.descendants(ASTTypeParameter::class.java).toList { it.typeMirror } + val (_, tvar) = acu.descendants(ASTTypeParameter::class.java).crossFindBoundaries().toList { it.typeMirror } val call = acu.descendants(ASTMethodCall::class.java).firstOrThrow() call.shouldMatchN { diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalInterfacesAndEnums.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalInterfacesAndEnums.java deleted file mode 100644 index cbc20db232..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalInterfacesAndEnums.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -public class LocalInterfacesAndEnums { - - { - class MyLocalClass {} - - // static local classes are not allowed (neither Java15 nor Java15 Preview) - //static class MyLocalStaticClass {} - - interface MyLocalInterface {} - - enum MyLocalEnum { A } - - // not supported anymore with Java16 - //@interface MyLocalAnnotation {} - } -} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalInterfacesAndEnums.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalInterfacesAndEnums.txt deleted file mode 100644 index c751077a6b..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalInterfacesAndEnums.txt +++ /dev/null @@ -1,21 +0,0 @@ -+- CompilationUnit[@PackageName = ""] - +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalInterfacesAndEnums", @CanonicalName = "LocalInterfacesAndEnums", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "false", @Local = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "LocalInterfacesAndEnums", @TopLevel = "true", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - +- ClassOrInterfaceBody[@Size = "1"] - +- Initializer[@Static = "false"] - +- Block[@Size = "3", @containsComment = "true"] - +- LocalClassStatement[] - | +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalInterfacesAndEnums$1MyLocalClass", @CanonicalName = null, @EffectiveVisibility = "local", @Enum = "false", @Final = "false", @Interface = "false", @Local = "true", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "MyLocalClass", @TopLevel = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- ClassOrInterfaceBody[@Size = "0"] - +- LocalClassStatement[] - | +- ClassOrInterfaceDeclaration[@Abstract = "true", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalInterfacesAndEnums$1MyLocalInterface", @CanonicalName = null, @EffectiveVisibility = "local", @Enum = "false", @Final = "false", @Interface = "true", @Local = "true", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "false", @RegularInterface = "true", @SimpleName = "MyLocalInterface", @TopLevel = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{abstract, static}", @ExplicitModifiers = "{}"] - | +- ClassOrInterfaceBody[@Size = "0"] - +- LocalClassStatement[] - +- EnumDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalInterfacesAndEnums$1MyLocalEnum", @CanonicalName = null, @EffectiveVisibility = "local", @Enum = "true", @Final = "true", @Interface = "false", @Local = "true", @Nested = "false", @PackageName = "", @Record = "false", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "MyLocalEnum", @TopLevel = "false", @Visibility = "local"] - +- ModifierList[@EffectiveModifiers = "{static, final}", @ExplicitModifiers = "{}"] - +- EnumBody[@SeparatorSemi = "false", @Size = "1", @TrailingComma = "false"] - +- EnumConstant[@AnonymousClass = "false", @EffectiveVisibility = "local", @Image = "A", @MethodName = "new", @Name = "A", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public, static, final}", @ExplicitModifiers = "{}"] - +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "true", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "A", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "true", @Visibility = "public"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.java deleted file mode 100644 index b860e99136..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -import java.util.stream.Collectors; -import java.util.List; - -/** - * @see JEP 384: Records (Second Preview) - */ -public class LocalRecords { - public interface Merchant {} - public static double computeSales(Merchant merchant, int month) { - return month; - } - List findTopMerchants(List merchants, int month) { - // Local record - record MerchantSales(Merchant merchant, double sales) {} - - return merchants.stream() - .map(merchant -> new MerchantSales(merchant, computeSales(merchant, month))) - .sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales())) - .map(MerchantSales::merchant) - .collect(Collectors.toList()); - } - - void methodWithLocalRecordAndModifiers() { - final record MyRecord1(String a) {} - final static record MyRecord2(String a) {} - @Deprecated record MyRecord3(String a) {} - final @Deprecated static record MyRecord4(String a) {} - } - - void statementThatStartsWithRecordAsRegularIdent() { - // https://github.com/pmd/pmd/issues/3145 - final Map record = new HashMap<>(); - record.put("key", "value"); - } - - void methodWithLocalClass() { - class MyLocalClass {} - } - - void methodWithLocalVarsNamedSealed() { - int result = 0; - int non = 1; - int sealed = 2; - result = non-sealed; - System.out.println(result); - } -} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.txt deleted file mode 100644 index 556d988450..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/LocalRecords.txt +++ /dev/null @@ -1,217 +0,0 @@ -+- CompilationUnit[@PackageName = ""] - +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.util.stream.Collectors", @ImportedSimpleName = "Collectors", @PackageName = "java.util.stream", @Static = "false"] - +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.util.List", @ImportedSimpleName = "List", @PackageName = "java.util", @Static = "false"] - +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalRecords", @CanonicalName = "LocalRecords", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "false", @Local = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "LocalRecords", @TopLevel = "true", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - +- ClassOrInterfaceBody[@Size = "7"] - +- ClassOrInterfaceDeclaration[@Abstract = "true", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalRecords$Merchant", @CanonicalName = "LocalRecords.Merchant", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "true", @Local = "false", @Nested = "true", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "false", @RegularInterface = "true", @SimpleName = "Merchant", @TopLevel = "false", @Visibility = "public"] - | +- ModifierList[@EffectiveModifiers = "{public, abstract, static}", @ExplicitModifiers = "{public}"] - | +- ClassOrInterfaceBody[@Size = "0"] - +- MethodDeclaration[@Abstract = "false", @Arity = "2", @EffectiveVisibility = "public", @Image = "computeSales", @Name = "computeSales", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "false"] - | +- ModifierList[@EffectiveModifiers = "{public, static}", @ExplicitModifiers = "{public, static}"] - | +- PrimitiveType[@Kind = "double"] - | +- FormalParameters[@Size = "2"] - | | +- FormalParameter[@EffectiveVisibility = "local", @Final = "false", @Varargs = "false", @Visibility = "local"] - | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Merchant"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "false", @LocalVariable = "false", @Name = "merchant", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- FormalParameter[@EffectiveVisibility = "local", @Final = "false", @Varargs = "false", @Visibility = "local"] - | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | +- PrimitiveType[@Kind = "int"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "false", @LocalVariable = "false", @Name = "month", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | +- Block[@Size = "1", @containsComment = "false"] - | +- ReturnStatement[] - | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "month", @Name = "month", @ParenthesisDepth = "0", @Parenthesized = "false"] - +- MethodDeclaration[@Abstract = "false", @Arity = "2", @EffectiveVisibility = "package", @Image = "findTopMerchants", @Name = "findTopMerchants", @Overridden = "false", @Varargs = "false", @Visibility = "package", @Void = "false"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "List"] - | | +- TypeArguments[@Diamond = "false", @Size = "1"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Merchant"] - | +- FormalParameters[@Size = "2"] - | | +- FormalParameter[@EffectiveVisibility = "local", @Final = "false", @Varargs = "false", @Visibility = "local"] - | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "List"] - | | | | +- TypeArguments[@Diamond = "false", @Size = "1"] - | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Merchant"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "false", @LocalVariable = "false", @Name = "merchants", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- FormalParameter[@EffectiveVisibility = "local", @Final = "false", @Varargs = "false", @Visibility = "local"] - | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | +- PrimitiveType[@Kind = "int"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "false", @LocalVariable = "false", @Name = "month", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | +- Block[@Size = "2", @containsComment = "false"] - | +- LocalClassStatement[] - | | +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalRecords$1MerchantSales", @CanonicalName = null, @EffectiveVisibility = "local", @Enum = "false", @Final = "true", @Interface = "false", @Local = "true", @Nested = "false", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "MerchantSales", @TopLevel = "false", @Visibility = "local"] - | | +- ModifierList[@EffectiveModifiers = "{static, final}", @ExplicitModifiers = "{}"] - | | +- RecordComponentList[@Size = "2", @Varargs = "false"] - | | | +- RecordComponent[@EffectiveVisibility = "local", @Varargs = "false", @Visibility = "private"] - | | | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Merchant"] - | | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "merchant", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | | | +- RecordComponent[@EffectiveVisibility = "local", @Varargs = "false", @Visibility = "private"] - | | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | +- PrimitiveType[@Kind = "double"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "sales", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | | +- RecordBody[@Size = "0"] - | +- ReturnStatement[] - | +- MethodCall[@CompileTimeConstant = "false", @Image = "collect", @MethodName = "collect", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- MethodCall[@CompileTimeConstant = "false", @Image = "map", @MethodName = "map", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- MethodCall[@CompileTimeConstant = "false", @Image = "sorted", @MethodName = "sorted", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- MethodCall[@CompileTimeConstant = "false", @Image = "map", @MethodName = "map", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- MethodCall[@CompileTimeConstant = "false", @Image = "stream", @MethodName = "stream", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "merchants", @Name = "merchants", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | | +- ArgumentList[@Size = "0"] - | | | | +- ArgumentList[@Size = "1"] - | | | | +- LambdaExpression[@BlockBody = "false", @CompileTimeConstant = "false", @ExpressionBody = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- LambdaParameterList[@Size = "1"] - | | | | | +- LambdaParameter[@EffectiveVisibility = "package", @Final = "false", @TypeInferred = "true", @Visibility = "package"] - | | | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "package", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "true", @LocalVariable = "false", @Name = "merchant", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "true", @Visibility = "package"] - | | | | +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "false", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] - | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "MerchantSales"] - | | | | +- ArgumentList[@Size = "2"] - | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "merchant", @Name = "merchant", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- MethodCall[@CompileTimeConstant = "false", @Image = "computeSales", @MethodName = "computeSales", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- ArgumentList[@Size = "2"] - | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "merchant", @Name = "merchant", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "month", @Name = "month", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- ArgumentList[@Size = "1"] - | | | +- LambdaExpression[@BlockBody = "false", @CompileTimeConstant = "false", @ExpressionBody = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- LambdaParameterList[@Size = "2"] - | | | | +- LambdaParameter[@EffectiveVisibility = "package", @Final = "false", @TypeInferred = "true", @Visibility = "package"] - | | | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "package", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "true", @LocalVariable = "false", @Name = "m1", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "true", @Visibility = "package"] - | | | | +- LambdaParameter[@EffectiveVisibility = "package", @Final = "false", @TypeInferred = "true", @Visibility = "package"] - | | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "package", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "true", @LocalVariable = "false", @Name = "m2", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "true", @Visibility = "package"] - | | | +- MethodCall[@CompileTimeConstant = "false", @Image = "compare", @MethodName = "compare", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Double"] - | | | +- ArgumentList[@Size = "2"] - | | | +- MethodCall[@CompileTimeConstant = "false", @Image = "sales", @MethodName = "sales", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "m2", @Name = "m2", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- ArgumentList[@Size = "0"] - | | | +- MethodCall[@CompileTimeConstant = "false", @Image = "sales", @MethodName = "sales", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "m1", @Name = "m1", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- ArgumentList[@Size = "0"] - | | +- ArgumentList[@Size = "1"] - | | +- MethodReference[@CompileTimeConstant = "false", @ConstructorReference = "false", @MethodName = "merchant", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "MerchantSales"] - | +- ArgumentList[@Size = "1"] - | +- MethodCall[@CompileTimeConstant = "false", @Image = "toList", @MethodName = "toList", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Collectors"] - | +- ArgumentList[@Size = "0"] - +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "package", @Image = "methodWithLocalRecordAndModifiers", @Name = "methodWithLocalRecordAndModifiers", @Overridden = "false", @Varargs = "false", @Visibility = "package", @Void = "true"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- VoidType[] - | +- FormalParameters[@Size = "0"] - | +- Block[@Size = "4", @containsComment = "false"] - | +- LocalClassStatement[] - | | +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalRecords$1MyRecord1", @CanonicalName = null, @EffectiveVisibility = "local", @Enum = "false", @Final = "true", @Interface = "false", @Local = "true", @Nested = "false", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "MyRecord1", @TopLevel = "false", @Visibility = "local"] - | | +- ModifierList[@EffectiveModifiers = "{static, final}", @ExplicitModifiers = "{final}"] - | | +- RecordComponentList[@Size = "1", @Varargs = "false"] - | | | +- RecordComponent[@EffectiveVisibility = "local", @Varargs = "false", @Visibility = "private"] - | | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "a", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | | +- RecordBody[@Size = "0"] - | +- LocalClassStatement[] - | | +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalRecords$1MyRecord2", @CanonicalName = null, @EffectiveVisibility = "local", @Enum = "false", @Final = "true", @Interface = "false", @Local = "true", @Nested = "false", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "MyRecord2", @TopLevel = "false", @Visibility = "local"] - | | +- ModifierList[@EffectiveModifiers = "{static, final}", @ExplicitModifiers = "{static, final}"] - | | +- RecordComponentList[@Size = "1", @Varargs = "false"] - | | | +- RecordComponent[@EffectiveVisibility = "local", @Varargs = "false", @Visibility = "private"] - | | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "a", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | | +- RecordBody[@Size = "0"] - | +- LocalClassStatement[] - | | +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalRecords$1MyRecord3", @CanonicalName = null, @EffectiveVisibility = "local", @Enum = "false", @Final = "true", @Interface = "false", @Local = "true", @Nested = "false", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "MyRecord3", @TopLevel = "false", @Visibility = "local"] - | | +- ModifierList[@EffectiveModifiers = "{static, final}", @ExplicitModifiers = "{}"] - | | | +- Annotation[@SimpleName = "Deprecated"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Deprecated"] - | | +- RecordComponentList[@Size = "1", @Varargs = "false"] - | | | +- RecordComponent[@EffectiveVisibility = "local", @Varargs = "false", @Visibility = "private"] - | | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "a", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | | +- RecordBody[@Size = "0"] - | +- LocalClassStatement[] - | +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalRecords$1MyRecord4", @CanonicalName = null, @EffectiveVisibility = "local", @Enum = "false", @Final = "true", @Interface = "false", @Local = "true", @Nested = "false", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "MyRecord4", @TopLevel = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{static, final}", @ExplicitModifiers = "{static, final}"] - | | +- Annotation[@SimpleName = "Deprecated"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Deprecated"] - | +- RecordComponentList[@Size = "1", @Varargs = "false"] - | | +- RecordComponent[@EffectiveVisibility = "local", @Varargs = "false", @Visibility = "private"] - | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "a", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | +- RecordBody[@Size = "0"] - +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "package", @Image = "statementThatStartsWithRecordAsRegularIdent", @Name = "statementThatStartsWithRecordAsRegularIdent", @Overridden = "false", @Varargs = "false", @Visibility = "package", @Void = "true"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- VoidType[] - | +- FormalParameters[@Size = "0"] - | +- Block[@Size = "2", @containsComment = "false"] - | +- LocalVariableDeclaration[@EffectiveVisibility = "local", @Final = "true", @TypeInferred = "false", @Visibility = "local"] - | | +- ModifierList[@EffectiveModifiers = "{final}", @ExplicitModifiers = "{final}"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Map"] - | | | +- TypeArguments[@Diamond = "false", @Size = "2"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | +- VariableDeclarator[@Initializer = "true", @Name = "record"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "true", @Name = "record", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "true", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "HashMap"] - | | | +- TypeArguments[@Diamond = "true", @Size = "0"] - | | +- ArgumentList[@Size = "0"] - | +- ExpressionStatement[] - | +- MethodCall[@CompileTimeConstant = "false", @Image = "put", @MethodName = "put", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "record", @Name = "record", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- ArgumentList[@Size = "2"] - | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "key", @Empty = "false", @Image = "\"key\"", @Length = "3", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "value", @Empty = "false", @Image = "\"value\"", @Length = "5", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "package", @Image = "methodWithLocalClass", @Name = "methodWithLocalClass", @Overridden = "false", @Varargs = "false", @Visibility = "package", @Void = "true"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- VoidType[] - | +- FormalParameters[@Size = "0"] - | +- Block[@Size = "1", @containsComment = "false"] - | +- LocalClassStatement[] - | +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalRecords$1MyLocalClass", @CanonicalName = null, @EffectiveVisibility = "local", @Enum = "false", @Final = "false", @Interface = "false", @Local = "true", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "MyLocalClass", @TopLevel = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- ClassOrInterfaceBody[@Size = "0"] - +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "package", @Image = "methodWithLocalVarsNamedSealed", @Name = "methodWithLocalVarsNamedSealed", @Overridden = "false", @Varargs = "false", @Visibility = "package", @Void = "true"] - +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - +- VoidType[] - +- FormalParameters[@Size = "0"] - +- Block[@Size = "5", @containsComment = "false"] - +- LocalVariableDeclaration[@EffectiveVisibility = "local", @Final = "false", @TypeInferred = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- PrimitiveType[@Kind = "int"] - | +- VariableDeclarator[@Initializer = "true", @Name = "result"] - | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "true", @Name = "result", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | +- NumericLiteral[@Base = "10", @CompileTimeConstant = "true", @DoubleLiteral = "false", @FloatLiteral = "false", @Image = "0", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @ValueAsDouble = "0.0", @ValueAsFloat = "0.0", @ValueAsInt = "0", @ValueAsLong = "0"] - +- LocalVariableDeclaration[@EffectiveVisibility = "local", @Final = "false", @TypeInferred = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- PrimitiveType[@Kind = "int"] - | +- VariableDeclarator[@Initializer = "true", @Name = "non"] - | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "true", @Name = "non", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | +- NumericLiteral[@Base = "10", @CompileTimeConstant = "true", @DoubleLiteral = "false", @FloatLiteral = "false", @Image = "1", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @ValueAsDouble = "1.0", @ValueAsFloat = "1.0", @ValueAsInt = "1", @ValueAsLong = "1"] - +- LocalVariableDeclaration[@EffectiveVisibility = "local", @Final = "false", @TypeInferred = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- PrimitiveType[@Kind = "int"] - | +- VariableDeclarator[@Initializer = "true", @Name = "sealed"] - | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "true", @Name = "sealed", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | +- NumericLiteral[@Base = "10", @CompileTimeConstant = "true", @DoubleLiteral = "false", @FloatLiteral = "false", @Image = "2", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @ValueAsDouble = "2.0", @ValueAsFloat = "2.0", @ValueAsInt = "2", @ValueAsLong = "2"] - +- ExpressionStatement[] - | +- AssignmentExpression[@CompileTimeConstant = "false", @Compound = "false", @Operator = "=", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- VariableAccess[@AccessType = "WRITE", @CompileTimeConstant = "false", @Image = "result", @Name = "result", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "-", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "non", @Name = "non", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "sealed", @Name = "sealed", @ParenthesisDepth = "0", @Parenthesized = "false"] - +- ExpressionStatement[] - +- MethodCall[@CompileTimeConstant = "false", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] - +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "System"] - +- ArgumentList[@Size = "1"] - +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "result", @Name = "result", @ParenthesisDepth = "0", @Parenthesized = "false"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/PatternMatchingInstanceof.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/PatternMatchingInstanceof.java deleted file mode 100644 index c541b5c8c0..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/PatternMatchingInstanceof.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * - * @see JEP 375: Pattern Matching for instanceof (Second Preview) - */ -public class PatternMatchingInstanceof { - private String s = "other string"; - - public void test() { - Object obj = "abc"; - //obj = 1; - if (obj instanceof String s) { - System.out.println("a) obj == s: " + (obj == s)); // true - } else { - System.out.println("b) obj == s: " + (obj == s)); // false - } - - if (!(obj instanceof String s)) { - System.out.println("c) obj == s: " + (obj == s)); // false - } else { - System.out.println("d) obj == s: " + (obj == s)); // true - } - - if (obj instanceof String s && s.length() > 2) { - System.out.println("e) obj == s: " + (obj == s)); // true - } - if (obj instanceof String s || s.length() > 5) { - System.out.println("f) obj == s: " + (obj == s)); // false - } - } - - public static void main(String[] args) { - new PatternMatchingInstanceof().test(); - } -} \ No newline at end of file diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/PatternMatchingInstanceof.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/PatternMatchingInstanceof.txt deleted file mode 100644 index b61f4a6c1a..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/PatternMatchingInstanceof.txt +++ /dev/null @@ -1,156 +0,0 @@ -+- CompilationUnit[@PackageName = ""] - +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "PatternMatchingInstanceof", @CanonicalName = "PatternMatchingInstanceof", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "false", @Local = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "PatternMatchingInstanceof", @TopLevel = "true", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - +- ClassOrInterfaceBody[@Size = "3"] - +- FieldDeclaration[@EffectiveVisibility = "private", @Visibility = "private"] - | +- ModifierList[@EffectiveModifiers = "{private}", @ExplicitModifiers = "{private}"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | +- VariableDeclarator[@Initializer = "true", @Name = "s"] - | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "true", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "other string", @Empty = "false", @Image = "\"other string\"", @Length = "12", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "public", @Image = "test", @Name = "test", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "true"] - | +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - | +- VoidType[] - | +- FormalParameters[@Size = "0"] - | +- Block[@Size = "5", @containsComment = "false"] - | +- LocalVariableDeclaration[@EffectiveVisibility = "local", @Final = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Object"] - | | +- VariableDeclarator[@Initializer = "true", @Name = "obj"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "true", @Name = "obj", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "abc", @Empty = "false", @Image = "\"abc\"", @Length = "3", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | +- IfStatement[@Else = "true"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] - | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- Block[@Size = "1", @containsComment = "true"] - | | | +- ExpressionStatement[] - | | | +- MethodCall[@CompileTimeConstant = "false", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "System"] - | | | +- ArgumentList[@Size = "1"] - | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "a) obj == s: ", @Empty = "false", @Image = "\"a) obj == s: \"", @Length = "13", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "==", @ParenthesisDepth = "1", @Parenthesized = "true"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- Block[@Size = "1", @containsComment = "true"] - | | +- ExpressionStatement[] - | | +- MethodCall[@CompileTimeConstant = "false", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "System"] - | | +- ArgumentList[@Size = "1"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "b) obj == s: ", @Empty = "false", @Image = "\"b) obj == s: \"", @Length = "13", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "==", @ParenthesisDepth = "1", @Parenthesized = "true"] - | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- IfStatement[@Else = "true"] - | | +- UnaryExpression[@CompileTimeConstant = "false", @Operator = "!", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "1", @Parenthesized = "true"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] - | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- Block[@Size = "1", @containsComment = "true"] - | | | +- ExpressionStatement[] - | | | +- MethodCall[@CompileTimeConstant = "false", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "System"] - | | | +- ArgumentList[@Size = "1"] - | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "c) obj == s: ", @Empty = "false", @Image = "\"c) obj == s: \"", @Length = "13", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "==", @ParenthesisDepth = "1", @Parenthesized = "true"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- Block[@Size = "1", @containsComment = "true"] - | | +- ExpressionStatement[] - | | +- MethodCall[@CompileTimeConstant = "false", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "System"] - | | +- ArgumentList[@Size = "1"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "d) obj == s: ", @Empty = "false", @Image = "\"d) obj == s: \"", @Length = "13", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "==", @ParenthesisDepth = "1", @Parenthesized = "true"] - | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- IfStatement[@Else = "false"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "&&", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] - | | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- MethodCall[@CompileTimeConstant = "false", @Image = "length", @MethodName = "length", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- ArgumentList[@Size = "0"] - | | | +- NumericLiteral[@Base = "10", @CompileTimeConstant = "true", @DoubleLiteral = "false", @FloatLiteral = "false", @Image = "2", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @ValueAsDouble = "2.0", @ValueAsFloat = "2.0", @ValueAsInt = "2", @ValueAsLong = "2"] - | | +- Block[@Size = "1", @containsComment = "true"] - | | +- ExpressionStatement[] - | | +- MethodCall[@CompileTimeConstant = "false", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "System"] - | | +- ArgumentList[@Size = "1"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "e) obj == s: ", @Empty = "false", @Image = "\"e) obj == s: \"", @Length = "13", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "==", @ParenthesisDepth = "1", @Parenthesized = "true"] - | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- IfStatement[@Else = "false"] - | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "||", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] - | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- MethodCall[@CompileTimeConstant = "false", @Image = "length", @MethodName = "length", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- ArgumentList[@Size = "0"] - | | +- NumericLiteral[@Base = "10", @CompileTimeConstant = "true", @DoubleLiteral = "false", @FloatLiteral = "false", @Image = "5", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @ValueAsDouble = "5.0", @ValueAsFloat = "5.0", @ValueAsInt = "5", @ValueAsLong = "5"] - | +- Block[@Size = "1", @containsComment = "true"] - | +- ExpressionStatement[] - | +- MethodCall[@CompileTimeConstant = "false", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "System"] - | +- ArgumentList[@Size = "1"] - | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "f) obj == s: ", @Empty = "false", @Image = "\"f) obj == s: \"", @Length = "13", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "==", @ParenthesisDepth = "1", @Parenthesized = "true"] - | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] - +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "public", @Image = "main", @MainMethod = "true", @Name = "main", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "true"] - +- ModifierList[@EffectiveModifiers = "{public, static}", @ExplicitModifiers = "{public, static}"] - +- VoidType[] - +- FormalParameters[@Size = "1"] - | +- FormalParameter[@EffectiveVisibility = "local", @Final = "false", @Varargs = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- ArrayType[@ArrayDepth = "1"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | +- ArrayDimensions[@Size = "1"] - | | +- ArrayTypeDim[@Varargs = "false"] - | +- VariableDeclaratorId[@ArrayType = "true", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "false", @LocalVariable = "false", @Name = "args", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - +- Block[@Size = "1", @containsComment = "false"] - +- ExpressionStatement[] - +- MethodCall[@CompileTimeConstant = "false", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] - +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "false", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "PatternMatchingInstanceof"] - | +- ArgumentList[@Size = "0"] - +- ArgumentList[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Point.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Point.java deleted file mode 100644 index bef547a6c6..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Point.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -/** - * @see JEP 384: Records (Second Preview) - */ -public record Point(int x, int y) { - - public static void main(String[] args) { - Point p = new Point(1, 2); - System.out.println("p = " + p); - } -} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Point.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Point.txt deleted file mode 100644 index a096029bbf..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Point.txt +++ /dev/null @@ -1,44 +0,0 @@ -+- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = "true"] - +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Point", @CanonicalName = "Point", @Enum = "false", @Image = "Point", @Interface = "false", @Local = "false", @Nested = "false", @PackageName = "", @Record = "true", @RegularClass = "false", @SimpleName = "Point", @TopLevel = "true", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public, final}", @ExplicitModifiers = "{public}"] - +- RecordComponentList[@Size = "2", @Varargs = "false"] - | +- RecordComponent[@Varargs = "false", @Visibility = "private"] - | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | +- PrimitiveType[@ArrayDepth = "0", @ArrayType = "false", @Boolean = "false", @ClassOrInterfaceType = "false", @Image = "int", @ModelConstant = "int", @PrimitiveType = "true", @ReferenceType = "false", @TypeImage = "int"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @FormalParameter = "false", @Image = "x", @LambdaParameter = "false", @LocalVariable = "false", @Name = "x", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @VariableName = "x", @Visibility = "private"] - | +- RecordComponent[@Varargs = "false", @Visibility = "private"] - | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | +- PrimitiveType[@ArrayDepth = "0", @ArrayType = "false", @Boolean = "false", @ClassOrInterfaceType = "false", @Image = "int", @ModelConstant = "int", @PrimitiveType = "true", @ReferenceType = "false", @TypeImage = "int"] - | +- VariableDeclaratorId[@ArrayType = "false", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @FormalParameter = "false", @Image = "y", @LambdaParameter = "false", @LocalVariable = "false", @Name = "y", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @VariableName = "y", @Visibility = "private"] - +- RecordBody[@Size = "1"] - +- MethodDeclaration[@Abstract = "false", @Arity = "1", @Image = "main", @MethodName = "main", @Name = "main", @Varargs = "false", @Visibility = "public", @Void = "true"] - +- ModifierList[@EffectiveModifiers = "{public, static}", @ExplicitModifiers = "{public, static}"] - +- ResultType[@Void = "true", @returnsArray = "false"] - +- FormalParameters[@Size = "1"] - | +- FormalParameter[@Varargs = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- ArrayType[@ArrayDepth = "1", @ArrayType = "true", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @ReferenceType = "true", @TypeImage = "String"] - | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @ReferenceType = "true", @SimpleName = "String", @TypeImage = "String"] - | | +- ArrayDimensions[@Size = "1"] - | | +- ArrayTypeDim[@Varargs = "false"] - | +- VariableDeclaratorId[@ArrayType = "true", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @FormalParameter = "true", @Image = "args", @LambdaParameter = "false", @LocalVariable = "false", @Name = "args", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @VariableName = "args", @Visibility = "package"] - +- Block[@Size = "2", @containsComment = "false"] - +- LocalVariableDeclaration[@TypeInferred = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @ReferenceType = "true", @SimpleName = "Point", @TypeImage = "Point"] - | +- VariableDeclarator[@Initializer = "true", @Name = "p"] - | +- VariableDeclaratorId[@ArrayType = "false", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @FormalParameter = "false", @Image = "p", @LambdaParameter = "false", @LocalVariable = "true", @Name = "p", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @VariableName = "p", @Visibility = "package"] - | +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @Diamond = "false", @DiamondTypeArgs = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] - | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @ReferenceType = "true", @SimpleName = "Point", @TypeImage = "Point"] - | +- ArgumentList[@Size = "2"] - | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "1", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "int", @StringLiteral = "false", @ValueAsDouble = "1.0", @ValueAsFloat = "1.0", @ValueAsInt = "1", @ValueAsLong = "1"] - | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "2", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "int", @StringLiteral = "false", @ValueAsDouble = "2.0", @ValueAsFloat = "2.0", @ValueAsInt = "2", @ValueAsLong = "2"] - +- ExpressionStatement[] - +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] - +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @ReferenceType = "true", @SimpleName = "System", @TypeImage = "System"] - +- ArgumentList[@Size = "1"] - +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] - +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "p = ", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = ""p = "", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] - +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "p", @Name = "p", @ParenthesisDepth = "0", @Parenthesized = "false"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Records.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Records.java deleted file mode 100644 index 27223d19b6..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Records.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -import java.io.IOException; -import java.lang.annotation.Target; -import java.lang.annotation.ElementType; - -/** - * @see JEP 384: Records (Second Preview) - */ -public class Records { - - @Target(ElementType.TYPE_USE) - @interface Nullable { } - - @Target({ElementType.CONSTRUCTOR, ElementType.PARAMETER}) - @interface MyAnnotation { } - - public record MyComplex(int real, @Deprecated int imaginary) { - // explicit declaration of a canonical constructor - @MyAnnotation - public MyComplex(@MyAnnotation int real, int imaginary) { - if (real > 100) throw new IllegalArgumentException("too big"); - this.real = real; - this.imaginary = imaginary; - } - - public record Nested(int a) {} - - public static class NestedClass { } - } - - - public record Range(int lo, int hi) { - // compact record constructor - @MyAnnotation - public Range { - if (lo > hi) /* referring here to the implicit constructor parameters */ - throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi)); - } - - public void foo() { } - } - - public record VarRec(@Nullable @Deprecated String @Nullable ... x) {} - - // note: Java 15 Preview allowed c-style arrays "public record ArrayRec(int x[]) {}" - // but PMD doesn't - public record ArrayRec(int[] x) {} - - public record EmptyRec() { - public void foo() { } - public Type bar() { return null; } - public static void baz() { - EmptyRec r = new EmptyRec<>(); - System.out.println(r); - } - } - - // see https://www.javaspecialists.eu/archive/Issue276.html - public interface Person { - String firstName(); - String lastName(); - } - public record PersonRecord(String firstName, String lastName) - implements Person, java.io.Serializable { - - } -} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Records.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Records.txt deleted file mode 100644 index f615b1a33e..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/Records.txt +++ /dev/null @@ -1,223 +0,0 @@ -+- CompilationUnit[@PackageName = ""] - +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.io.IOException", @ImportedSimpleName = "IOException", @PackageName = "java.io", @Static = "false"] - +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.Target", @ImportedSimpleName = "Target", @PackageName = "java.lang.annotation", @Static = "false"] - +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType", @ImportedSimpleName = "ElementType", @PackageName = "java.lang.annotation", @Static = "false"] - +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records", @CanonicalName = "Records", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "false", @Local = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "Records", @TopLevel = "true", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - +- ClassOrInterfaceBody[@Size = "9"] - +- AnnotationTypeDeclaration[@Abstract = "true", @Annotation = "true", @Anonymous = "false", @BinaryName = "Records$Nullable", @CanonicalName = "Records.Nullable", @EffectiveVisibility = "package", @Enum = "false", @Final = "false", @Interface = "true", @Local = "false", @Nested = "true", @PackageName = "", @Record = "false", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "Nullable", @TopLevel = "false", @Visibility = "package"] - | +- ModifierList[@EffectiveModifiers = "{abstract, static}", @ExplicitModifiers = "{}"] - | | +- Annotation[@SimpleName = "Target"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Target"] - | | +- AnnotationMemberList[@Size = "1"] - | | +- MemberValuePair[@Image = "value", @Name = "value", @Shorthand = "true"] - | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "TYPE_USE", @Name = "TYPE_USE", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "ElementType"] - | +- AnnotationTypeBody[@Size = "0"] - +- AnnotationTypeDeclaration[@Abstract = "true", @Annotation = "true", @Anonymous = "false", @BinaryName = "Records$MyAnnotation", @CanonicalName = "Records.MyAnnotation", @EffectiveVisibility = "package", @Enum = "false", @Final = "false", @Interface = "true", @Local = "false", @Nested = "true", @PackageName = "", @Record = "false", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "MyAnnotation", @TopLevel = "false", @Visibility = "package"] - | +- ModifierList[@EffectiveModifiers = "{abstract, static}", @ExplicitModifiers = "{}"] - | | +- Annotation[@SimpleName = "Target"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Target"] - | | +- AnnotationMemberList[@Size = "1"] - | | +- MemberValuePair[@Image = "value", @Name = "value", @Shorthand = "true"] - | | +- MemberValueArrayInitializer[] - | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "CONSTRUCTOR", @Name = "CONSTRUCTOR", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "ElementType"] - | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "PARAMETER", @Name = "PARAMETER", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "ElementType"] - | +- AnnotationTypeBody[@Size = "0"] - +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records$MyComplex", @CanonicalName = "Records.MyComplex", @EffectiveVisibility = "public", @Enum = "false", @Final = "true", @Interface = "false", @Local = "false", @Nested = "true", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "MyComplex", @TopLevel = "false", @Visibility = "public"] - | +- ModifierList[@EffectiveModifiers = "{public, static, final}", @ExplicitModifiers = "{public}"] - | +- RecordComponentList[@Size = "2", @Varargs = "false"] - | | +- RecordComponent[@EffectiveVisibility = "private", @Varargs = "false", @Visibility = "private"] - | | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | +- PrimitiveType[@Kind = "int"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "real", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | | +- RecordComponent[@EffectiveVisibility = "private", @Varargs = "false", @Visibility = "private"] - | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | +- Annotation[@SimpleName = "Deprecated"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Deprecated"] - | | +- PrimitiveType[@Kind = "int"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "imaginary", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | +- RecordBody[@Size = "3"] - | +- ConstructorDeclaration[@Abstract = "false", @Arity = "2", @EffectiveVisibility = "public", @Image = "MyComplex", @Name = "MyComplex", @Varargs = "false", @Visibility = "public", @containsComment = "false"] - | | +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - | | | +- Annotation[@SimpleName = "MyAnnotation"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "MyAnnotation"] - | | +- FormalParameters[@Size = "2"] - | | | +- FormalParameter[@EffectiveVisibility = "local", @Final = "false", @Varargs = "false", @Visibility = "local"] - | | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | | | +- Annotation[@SimpleName = "MyAnnotation"] - | | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "MyAnnotation"] - | | | | +- PrimitiveType[@Kind = "int"] - | | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "false", @LocalVariable = "false", @Name = "real", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | | +- FormalParameter[@EffectiveVisibility = "local", @Final = "false", @Varargs = "false", @Visibility = "local"] - | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | | +- PrimitiveType[@Kind = "int"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "false", @LocalVariable = "false", @Name = "imaginary", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- Block[@Size = "3", @containsComment = "false"] - | | +- IfStatement[@Else = "false"] - | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "real", @Name = "real", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- NumericLiteral[@Base = "10", @CompileTimeConstant = "true", @DoubleLiteral = "false", @FloatLiteral = "false", @Image = "100", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @ValueAsDouble = "100.0", @ValueAsFloat = "100.0", @ValueAsInt = "100", @ValueAsLong = "100"] - | | | +- ThrowStatement[] - | | | +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "false", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "IllegalArgumentException"] - | | | +- ArgumentList[@Size = "1"] - | | | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "too big", @Empty = "false", @Image = "\"too big\"", @Length = "7", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | | +- ExpressionStatement[] - | | | +- AssignmentExpression[@CompileTimeConstant = "false", @Compound = "false", @Operator = "=", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- FieldAccess[@AccessType = "WRITE", @CompileTimeConstant = "false", @Image = "real", @Name = "real", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- ThisExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "real", @Name = "real", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- ExpressionStatement[] - | | +- AssignmentExpression[@CompileTimeConstant = "false", @Compound = "false", @Operator = "=", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- FieldAccess[@AccessType = "WRITE", @CompileTimeConstant = "false", @Image = "imaginary", @Name = "imaginary", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- ThisExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "imaginary", @Name = "imaginary", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records$MyComplex$Nested", @CanonicalName = "Records.MyComplex.Nested", @EffectiveVisibility = "public", @Enum = "false", @Final = "true", @Interface = "false", @Local = "false", @Nested = "true", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "Nested", @TopLevel = "false", @Visibility = "public"] - | | +- ModifierList[@EffectiveModifiers = "{public, static, final}", @ExplicitModifiers = "{public}"] - | | +- RecordComponentList[@Size = "1", @Varargs = "false"] - | | | +- RecordComponent[@EffectiveVisibility = "private", @Varargs = "false", @Visibility = "private"] - | | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | +- PrimitiveType[@Kind = "int"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "a", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | | +- RecordBody[@Size = "0"] - | +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records$MyComplex$NestedClass", @CanonicalName = "Records.MyComplex.NestedClass", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "false", @Local = "false", @Nested = "true", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "NestedClass", @TopLevel = "false", @Visibility = "public"] - | +- ModifierList[@EffectiveModifiers = "{public, static}", @ExplicitModifiers = "{public, static}"] - | +- ClassOrInterfaceBody[@Size = "0"] - +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records$Range", @CanonicalName = "Records.Range", @EffectiveVisibility = "public", @Enum = "false", @Final = "true", @Interface = "false", @Local = "false", @Nested = "true", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "Range", @TopLevel = "false", @Visibility = "public"] - | +- ModifierList[@EffectiveModifiers = "{public, static, final}", @ExplicitModifiers = "{public}"] - | +- RecordComponentList[@Size = "2", @Varargs = "false"] - | | +- RecordComponent[@EffectiveVisibility = "private", @Varargs = "false", @Visibility = "private"] - | | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | +- PrimitiveType[@Kind = "int"] - | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "lo", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | | +- RecordComponent[@EffectiveVisibility = "private", @Varargs = "false", @Visibility = "private"] - | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | +- PrimitiveType[@Kind = "int"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "hi", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | +- RecordBody[@Size = "2"] - | +- CompactConstructorDeclaration[@EffectiveVisibility = "public", @Image = "Range", @Visibility = "public"] - | | +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - | | | +- Annotation[@SimpleName = "MyAnnotation"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "MyAnnotation"] - | | +- Block[@Size = "1", @containsComment = "false"] - | | +- IfStatement[@Else = "false"] - | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "lo", @Name = "lo", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "hi", @Name = "hi", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- ThrowStatement[] - | | +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "false", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "IllegalArgumentException"] - | | +- ArgumentList[@Size = "1"] - | | +- MethodCall[@CompileTimeConstant = "false", @Image = "format", @MethodName = "format", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | +- ArgumentList[@Size = "3"] - | | +- StringLiteral[@CompileTimeConstant = "true", @ConstValue = "(%d,%d)", @Empty = "false", @Image = "\"(%d,%d)\"", @Length = "7", @ParenthesisDepth = "0", @Parenthesized = "false", @TextBlock = "false"] - | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "lo", @Name = "lo", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "hi", @Name = "hi", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "public", @Image = "foo", @Name = "foo", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "true"] - | +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - | +- VoidType[] - | +- FormalParameters[@Size = "0"] - | +- Block[@Size = "0", @containsComment = "false"] - +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records$VarRec", @CanonicalName = "Records.VarRec", @EffectiveVisibility = "public", @Enum = "false", @Final = "true", @Interface = "false", @Local = "false", @Nested = "true", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "VarRec", @TopLevel = "false", @Visibility = "public"] - | +- ModifierList[@EffectiveModifiers = "{public, static, final}", @ExplicitModifiers = "{public}"] - | +- RecordComponentList[@Size = "1", @Varargs = "true"] - | | +- RecordComponent[@EffectiveVisibility = "private", @Varargs = "true", @Visibility = "private"] - | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | | +- Annotation[@SimpleName = "Nullable"] - | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Nullable"] - | | | +- Annotation[@SimpleName = "Deprecated"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Deprecated"] - | | +- ArrayType[@ArrayDepth = "1"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | | +- ArrayDimensions[@Size = "1"] - | | | +- ArrayTypeDim[@Varargs = "true"] - | | | +- Annotation[@SimpleName = "Nullable"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Nullable"] - | | +- VariableDeclaratorId[@ArrayType = "true", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "x", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | +- RecordBody[@Size = "0"] - +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records$ArrayRec", @CanonicalName = "Records.ArrayRec", @EffectiveVisibility = "public", @Enum = "false", @Final = "true", @Interface = "false", @Local = "false", @Nested = "true", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "ArrayRec", @TopLevel = "false", @Visibility = "public"] - | +- ModifierList[@EffectiveModifiers = "{public, static, final}", @ExplicitModifiers = "{public}"] - | +- RecordComponentList[@Size = "1", @Varargs = "false"] - | | +- RecordComponent[@EffectiveVisibility = "private", @Varargs = "false", @Visibility = "private"] - | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | +- ArrayType[@ArrayDepth = "1"] - | | | +- PrimitiveType[@Kind = "int"] - | | | +- ArrayDimensions[@Size = "1"] - | | | +- ArrayTypeDim[@Varargs = "false"] - | | +- VariableDeclaratorId[@ArrayType = "true", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "x", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | +- RecordBody[@Size = "0"] - +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records$EmptyRec", @CanonicalName = "Records.EmptyRec", @EffectiveVisibility = "public", @Enum = "false", @Final = "true", @Interface = "false", @Local = "false", @Nested = "true", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "EmptyRec", @TopLevel = "false", @Visibility = "public"] - | +- ModifierList[@EffectiveModifiers = "{public, static, final}", @ExplicitModifiers = "{public}"] - | +- TypeParameters[@Size = "1"] - | | +- TypeParameter[@Image = "Type", @Name = "Type", @TypeBound = "false"] - | +- RecordComponentList[@Size = "0", @Varargs = "false"] - | +- RecordBody[@Size = "3"] - | +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "public", @Image = "foo", @Name = "foo", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "true"] - | | +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - | | +- VoidType[] - | | +- FormalParameters[@Size = "0"] - | | +- Block[@Size = "0", @containsComment = "false"] - | +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "public", @Image = "bar", @Name = "bar", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "false"] - | | +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Type"] - | | +- FormalParameters[@Size = "0"] - | | +- Block[@Size = "1", @containsComment = "false"] - | | +- ReturnStatement[] - | | +- NullLiteral[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "public", @Image = "baz", @Name = "baz", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "true"] - | +- ModifierList[@EffectiveModifiers = "{public, static}", @ExplicitModifiers = "{public, static}"] - | +- VoidType[] - | +- FormalParameters[@Size = "0"] - | +- Block[@Size = "2", @containsComment = "false"] - | +- LocalVariableDeclaration[@EffectiveVisibility = "local", @Final = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "EmptyRec"] - | | | +- TypeArguments[@Diamond = "false", @Size = "1"] - | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | +- VariableDeclarator[@Initializer = "true", @Name = "r"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "true", @Name = "r", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - | | +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "true", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "EmptyRec"] - | | | +- TypeArguments[@Diamond = "true", @Size = "0"] - | | +- ArgumentList[@Size = "0"] - | +- ExpressionStatement[] - | +- MethodCall[@CompileTimeConstant = "false", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] - | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "System"] - | +- ArgumentList[@Size = "1"] - | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "r", @Name = "r", @ParenthesisDepth = "0", @Parenthesized = "false"] - +- ClassOrInterfaceDeclaration[@Abstract = "true", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records$Person", @CanonicalName = "Records.Person", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "true", @Local = "false", @Nested = "true", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "false", @RegularInterface = "true", @SimpleName = "Person", @TopLevel = "false", @Visibility = "public"] - | +- ModifierList[@EffectiveModifiers = "{public, abstract, static}", @ExplicitModifiers = "{public}"] - | +- ClassOrInterfaceBody[@Size = "2"] - | +- MethodDeclaration[@Abstract = "true", @Arity = "0", @EffectiveVisibility = "public", @Image = "firstName", @Name = "firstName", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "false"] - | | +- ModifierList[@EffectiveModifiers = "{public, abstract}", @ExplicitModifiers = "{}"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | +- FormalParameters[@Size = "0"] - | +- MethodDeclaration[@Abstract = "true", @Arity = "0", @EffectiveVisibility = "public", @Image = "lastName", @Name = "lastName", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "false"] - | +- ModifierList[@EffectiveModifiers = "{public, abstract}", @ExplicitModifiers = "{}"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | +- FormalParameters[@Size = "0"] - +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Records$PersonRecord", @CanonicalName = "Records.PersonRecord", @EffectiveVisibility = "public", @Enum = "false", @Final = "true", @Interface = "false", @Local = "false", @Nested = "true", @PackageName = "", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "PersonRecord", @TopLevel = "false", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public, static, final}", @ExplicitModifiers = "{public}"] - +- RecordComponentList[@Size = "2", @Varargs = "false"] - | +- RecordComponent[@EffectiveVisibility = "private", @Varargs = "false", @Visibility = "private"] - | | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "firstName", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - | +- RecordComponent[@EffectiveVisibility = "private", @Varargs = "false", @Visibility = "private"] - | +- ModifierList[@EffectiveModifiers = "{private, final}", @ExplicitModifiers = "{}"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "lastName", @PatternBinding = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "private"] - +- ImplementsList[@Size = "2"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Person"] - | +- ClassOrInterfaceType[@FullyQualified = "true", @SimpleName = "Serializable"] - +- RecordBody[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/Expr.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/Expr.txt deleted file mode 100644 index 957d515ffa..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/Expr.txt +++ /dev/null @@ -1,11 +0,0 @@ -+- CompilationUnit[@PackageName = "com.example.expression"] - +- PackageDeclaration[@Name = "com.example.expression"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - +- ClassOrInterfaceDeclaration[@Abstract = "true", @Annotation = "false", @Anonymous = "false", @BinaryName = "com.example.expression.Expr", @CanonicalName = "com.example.expression.Expr", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "true", @Local = "false", @Nested = "false", @PackageName = "com.example.expression", @PackagePrivate = "false", @Record = "false", @RegularClass = "false", @RegularInterface = "true", @SimpleName = "Expr", @TopLevel = "true", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public, sealed, abstract}", @ExplicitModifiers = "{public, sealed}"] - +- PermitsList[@Size = "4"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "ConstantExpr"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "PlusExpr"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "TimesExpr"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "NegExpr"] - +- ClassOrInterfaceBody[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Shape.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Shape.txt deleted file mode 100644 index eb8de441c6..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Shape.txt +++ /dev/null @@ -1,10 +0,0 @@ -+- CompilationUnit[@PackageName = "com.example.geometry"] - +- PackageDeclaration[@Name = "com.example.geometry"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "com.example.geometry.Shape", @CanonicalName = "com.example.geometry.Shape", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "false", @Local = "false", @Nested = "false", @PackageName = "com.example.geometry", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "Shape", @TopLevel = "true", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public, sealed}", @ExplicitModifiers = "{public, sealed}"] - +- PermitsList[@Size = "3"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Circle"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Rectangle"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Square"] - +- ClassOrInterfaceBody[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Square.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Square.txt deleted file mode 100644 index 013d84bc08..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Square.txt +++ /dev/null @@ -1,8 +0,0 @@ -+- CompilationUnit[@PackageName = "com.example.geometry"] - +- PackageDeclaration[@Name = "com.example.geometry"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "com.example.geometry.Square", @CanonicalName = "com.example.geometry.Square", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "false", @Local = "false", @Nested = "false", @PackageName = "com.example.geometry", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "Square", @TopLevel = "true", @Visibility = "public"] - +- ModifierList[@EffectiveModifiers = "{public, non-sealed}", @ExplicitModifiers = "{public, non-sealed}"] - +- ExtendsList[@Size = "1"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Shape"] - +- ClassOrInterfaceBody[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java16/PatternMatchingInstanceof.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java16/PatternMatchingInstanceof.java index c4e6e46e5a..c801c09429 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java16/PatternMatchingInstanceof.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java16/PatternMatchingInstanceof.java @@ -1,3 +1,15 @@ +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.MODULE; +import static java.lang.annotation.ElementType.PACKAGE; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + /** * * @see JEP 394: Pattern Matching for instanceof @@ -52,4 +64,15 @@ public class PatternMatchingInstanceof { public static void main(String[] args) { new PatternMatchingInstanceof().test(); } + + // InstanceofExpression can be annotated + class Foo { + { + Object f = null; + Object o = f instanceof @Nullable Foo; + } + } + + @Target(value=ElementType.TYPE_USE) + @interface Nullable { } } \ No newline at end of file diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java16/PatternMatchingInstanceof.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java16/PatternMatchingInstanceof.txt index 062e2cd648..f76c860d60 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java16/PatternMatchingInstanceof.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java16/PatternMatchingInstanceof.txt @@ -1,7 +1,17 @@ +- CompilationUnit[@PackageName = ""] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType.CONSTRUCTOR", @ImportedSimpleName = "CONSTRUCTOR", @PackageName = "java.lang.annotation.ElementType", @Static = "true"] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType.FIELD", @ImportedSimpleName = "FIELD", @PackageName = "java.lang.annotation.ElementType", @Static = "true"] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType.LOCAL_VARIABLE", @ImportedSimpleName = "LOCAL_VARIABLE", @PackageName = "java.lang.annotation.ElementType", @Static = "true"] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType.METHOD", @ImportedSimpleName = "METHOD", @PackageName = "java.lang.annotation.ElementType", @Static = "true"] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType.MODULE", @ImportedSimpleName = "MODULE", @PackageName = "java.lang.annotation.ElementType", @Static = "true"] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType.PACKAGE", @ImportedSimpleName = "PACKAGE", @PackageName = "java.lang.annotation.ElementType", @Static = "true"] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType.PARAMETER", @ImportedSimpleName = "PARAMETER", @PackageName = "java.lang.annotation.ElementType", @Static = "true"] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType.TYPE", @ImportedSimpleName = "TYPE", @PackageName = "java.lang.annotation.ElementType", @Static = "true"] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.ElementType", @ImportedSimpleName = "ElementType", @PackageName = "java.lang.annotation", @Static = "false"] + +- ImportDeclaration[@ImportOnDemand = "false", @ImportedName = "java.lang.annotation.Target", @ImportedSimpleName = "Target", @PackageName = "java.lang.annotation", @Static = "false"] +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "PatternMatchingInstanceof", @CanonicalName = "PatternMatchingInstanceof", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Interface = "false", @Local = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "PatternMatchingInstanceof", @TopLevel = "true", @Visibility = "public"] +- ModifierList[@EffectiveModifiers = "{public}", @ExplicitModifiers = "{public}"] - +- ClassOrInterfaceBody[@Size = "3"] + +- ClassOrInterfaceBody[@Size = "5"] +- FieldDeclaration[@EffectiveVisibility = "private", @Visibility = "private"] | +- ModifierList[@EffectiveModifiers = "{private}", @ExplicitModifiers = "{private}"] | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] @@ -23,7 +33,7 @@ | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] + | | | +- TypePattern[@EffectiveVisibility = "package", @ParenthesisDepth = "0", @Visibility = "package"] | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] @@ -75,7 +85,7 @@ | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "1", @Parenthesized = "true"] | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] + | | | +- TypePattern[@EffectiveVisibility = "package", @ParenthesisDepth = "0", @Visibility = "package"] | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] @@ -108,7 +118,7 @@ | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] + | | | | +- TypePattern[@EffectiveVisibility = "package", @ParenthesisDepth = "0", @Visibility = "package"] | | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] | | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] @@ -134,7 +144,7 @@ | | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] + | | | | +- TypePattern[@EffectiveVisibility = "package", @ParenthesisDepth = "0", @Visibility = "package"] | | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] | | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] @@ -159,7 +169,7 @@ | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] + | | | +- TypePattern[@EffectiveVisibility = "package", @ParenthesisDepth = "0", @Visibility = "package"] | | | +- ModifierList[@EffectiveModifiers = "{final}", @ExplicitModifiers = "{final}"] | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] | | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @PatternBinding = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] @@ -191,7 +201,7 @@ | | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] | | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] + | | | +- TypePattern[@EffectiveVisibility = "package", @ParenthesisDepth = "0", @Visibility = "package"] | | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] | | | | +- Annotation[@SimpleName = "Deprecated"] | | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Deprecated"] @@ -225,7 +235,7 @@ | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] | | +- PatternExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] - | | +- TypePattern[@EffectiveVisibility = "package", @Visibility = "package"] + | | +- TypePattern[@EffectiveVisibility = "package", @ParenthesisDepth = "0", @Visibility = "package"] | | +- ModifierList[@EffectiveModifiers = "{final}", @ExplicitModifiers = "{final}"] | | | +- Annotation[@SimpleName = "Deprecated"] | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Deprecated"] @@ -256,20 +266,52 @@ | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "obj", @Name = "obj", @ParenthesisDepth = "0", @Parenthesized = "false"] | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "public", @Image = "main", @MainMethod = "true", @Name = "main", @Overridden = "false", @Varargs = "false", @Visibility = "public", @Void = "true"] - +- ModifierList[@EffectiveModifiers = "{public, static}", @ExplicitModifiers = "{public, static}"] - +- VoidType[] - +- FormalParameters[@Size = "1"] - | +- FormalParameter[@EffectiveVisibility = "local", @Final = "false", @Varargs = "false", @Visibility = "local"] - | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] - | +- ArrayType[@ArrayDepth = "1"] - | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] - | | +- ArrayDimensions[@Size = "1"] - | | +- ArrayTypeDim[@Varargs = "false"] - | +- VariableDeclaratorId[@ArrayType = "true", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "false", @LocalVariable = "false", @Name = "args", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] - +- Block[@Size = "1", @containsComment = "false"] - +- ExpressionStatement[] - +- MethodCall[@CompileTimeConstant = "false", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] - +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "false", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] - | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "PatternMatchingInstanceof"] - | +- ArgumentList[@Size = "0"] - +- ArgumentList[@Size = "0"] + | +- ModifierList[@EffectiveModifiers = "{public, static}", @ExplicitModifiers = "{public, static}"] + | +- VoidType[] + | +- FormalParameters[@Size = "1"] + | | +- FormalParameter[@EffectiveVisibility = "local", @Final = "false", @Varargs = "false", @Visibility = "local"] + | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] + | | +- ArrayType[@ArrayDepth = "1"] + | | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "String"] + | | | +- ArrayDimensions[@Size = "1"] + | | | +- ArrayTypeDim[@Varargs = "false"] + | | +- VariableDeclaratorId[@ArrayType = "true", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @LambdaParameter = "false", @LocalVariable = "false", @Name = "args", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] + | +- Block[@Size = "1", @containsComment = "false"] + | +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "false", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] + | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "PatternMatchingInstanceof"] + | | +- ArgumentList[@Size = "0"] + | +- ArgumentList[@Size = "0"] + +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "PatternMatchingInstanceof$Foo", @CanonicalName = "PatternMatchingInstanceof.Foo", @EffectiveVisibility = "package", @Enum = "false", @Final = "false", @Interface = "false", @Local = "false", @Nested = "true", @PackageName = "", @PackagePrivate = "true", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "Foo", @TopLevel = "false", @Visibility = "package"] + | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] + | +- ClassOrInterfaceBody[@Size = "1"] + | +- Initializer[@Static = "false"] + | +- Block[@Size = "2", @containsComment = "false"] + | +- LocalVariableDeclaration[@EffectiveVisibility = "local", @Final = "false", @TypeInferred = "false", @Visibility = "local"] + | | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] + | | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Object"] + | | +- VariableDeclarator[@Initializer = "true", @Name = "f"] + | | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "true", @Name = "f", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] + | | +- NullLiteral[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- LocalVariableDeclaration[@EffectiveVisibility = "local", @Final = "false", @TypeInferred = "false", @Visibility = "local"] + | +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"] + | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Object"] + | +- VariableDeclarator[@Initializer = "true", @Name = "o"] + | +- VariableDeclaratorId[@ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @LambdaParameter = "false", @LocalVariable = "true", @Name = "o", @PatternBinding = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @TypeInferred = "false", @Visibility = "local"] + | +- InfixExpression[@CompileTimeConstant = "false", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "f", @Name = "f", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Foo"] + | +- Annotation[@SimpleName = "Nullable"] + | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Nullable"] + +- AnnotationTypeDeclaration[@Abstract = "true", @Annotation = "true", @Anonymous = "false", @BinaryName = "PatternMatchingInstanceof$Nullable", @CanonicalName = "PatternMatchingInstanceof.Nullable", @EffectiveVisibility = "package", @Enum = "false", @Final = "false", @Interface = "true", @Local = "false", @Nested = "true", @PackageName = "", @Record = "false", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "Nullable", @TopLevel = "false", @Visibility = "package"] + +- ModifierList[@EffectiveModifiers = "{abstract, static}", @ExplicitModifiers = "{}"] + | +- Annotation[@SimpleName = "Target"] + | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "Target"] + | +- AnnotationMemberList[@Size = "1"] + | +- MemberValuePair[@Image = "value", @Name = "value", @Shorthand = "false"] + | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Image = "TYPE_USE", @Name = "TYPE_USE", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- TypeExpression[@CompileTimeConstant = "false", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ClassOrInterfaceType[@FullyQualified = "false", @SimpleName = "ElementType"] + +- AnnotationTypeBody[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/LocalVars.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/LocalVars.java new file mode 100644 index 0000000000..2e109cd022 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/LocalVars.java @@ -0,0 +1,15 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +public class LocalVars { + + public void aMethod() { + String sealed = null; + + sealed = this.getClass().getName(); + + // error: sealed or non-sealed local classes are not allowed + // sealed class LocalSealedClass {} + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/LocalVars.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/LocalVars.txt new file mode 100644 index 0000000000..810d59869c --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/LocalVars.txt @@ -0,0 +1,23 @@ ++- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = "true"] + +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "LocalVars", @CanonicalName = "LocalVars", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Image = "LocalVars", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "LocalVars", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "public", @Volatile = "false"] + +- ModifierList[] + +- ClassOrInterfaceBody[@Size = "1"] + +- MethodDeclaration[@Abstract = "false", @Arity = "0", @EffectiveVisibility = "public", @Final = "false", @Image = "aMethod", @MainMethod = "false", @MethodName = "aMethod", @Name = "aMethod", @Native = "false", @Overridden = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "public", @Void = "true", @Volatile = "false"] + +- ModifierList[] + +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + +- FormalParameters[@Size = "0"] + +- Block[@Size = "2", @containsComment = "true"] + +- LocalVariableDeclaration[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @Visibility = "local", @Volatile = "false"] + | +- ModifierList[] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | +- VariableDeclarator[@Initializer = "true", @Name = "sealed"] + | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "sealed", @LambdaParameter = "false", @LocalVariable = "true", @Name = "sealed", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "sealed", @Visibility = "local", @Volatile = "false"] + | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + +- ExpressionStatement[] + +- AssignmentExpression[@CompileTimeConstant = "false", @Compound = "false", @Expression = "true", @Operator = "=", @ParenthesisDepth = "0", @Parenthesized = "false"] + +- VariableAccess[@AccessType = "WRITE", @CompileTimeConstant = "false", @Expression = "true", @Image = "sealed", @Name = "sealed", @ParenthesisDepth = "0", @Parenthesized = "false"] + +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "getName", @MethodName = "getName", @ParenthesisDepth = "0", @Parenthesized = "false"] + +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "getClass", @MethodName = "getClass", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ThisExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "0"] + +- ArgumentList[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/ConstantExpr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/ConstantExpr.java similarity index 66% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/ConstantExpr.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/ConstantExpr.java index 800fc3d2d1..22d335c466 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/ConstantExpr.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/ConstantExpr.java @@ -4,6 +4,6 @@ package com.example.expression; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public final class ConstantExpr implements Expr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/Expr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.java similarity index 70% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/Expr.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.java index 7218bd477f..5be7b03c0a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/Expr.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.java @@ -4,7 +4,7 @@ package com.example.expression; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public sealed interface Expr permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.txt new file mode 100644 index 0000000000..c0e0382077 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/Expr.txt @@ -0,0 +1,11 @@ ++- CompilationUnit[@PackageName = "com.example.expression", @declarationsAreInDefaultPackage = "false"] + +- PackageDeclaration[@Name = "com.example.expression"] + | +- ModifierList[] + +- ClassOrInterfaceDeclaration[@Abstract = "true", @Annotation = "false", @Anonymous = "false", @BinaryName = "com.example.expression.Expr", @CanonicalName = "com.example.expression.Expr", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Image = "Expr", @Interface = "true", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "com.example.expression", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Record = "false", @RegularClass = "false", @RegularInterface = "true", @SimpleName = "Expr", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "public", @Volatile = "false"] + +- ModifierList[] + +- PermitsList[@Size = "4"] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "ConstantExpr", @TypeImage = "ConstantExpr"] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "PlusExpr", @TypeImage = "PlusExpr"] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "TimesExpr", @TypeImage = "TimesExpr"] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "NegExpr", @TypeImage = "NegExpr"] + +- ClassOrInterfaceBody[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/NegExpr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/NegExpr.java similarity index 66% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/NegExpr.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/NegExpr.java index 35194d1cb9..416645a6a1 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/NegExpr.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/NegExpr.java @@ -4,6 +4,6 @@ package com.example.expression; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public final class NegExpr implements Expr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/PlusExpr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/PlusExpr.java similarity index 66% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/PlusExpr.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/PlusExpr.java index ac652798e2..13b2cbb8fb 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/PlusExpr.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/PlusExpr.java @@ -4,6 +4,6 @@ package com.example.expression; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public final class PlusExpr implements Expr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/TimesExpr.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/TimesExpr.java similarity index 66% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/TimesExpr.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/TimesExpr.java index a5cf1ccc74..cdde6fa214 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/expression/TimesExpr.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/expression/TimesExpr.java @@ -4,6 +4,6 @@ package com.example.expression; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public final class TimesExpr implements Expr { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Circle.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Circle.java similarity index 65% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Circle.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Circle.java index c4baabda9b..feca615807 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Circle.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Circle.java @@ -4,6 +4,6 @@ package com.example.geometry; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public final class Circle extends Shape { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/FilledRectangle.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/FilledRectangle.java similarity index 67% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/FilledRectangle.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/FilledRectangle.java index 92eb290b6b..198ce630d4 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/FilledRectangle.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/FilledRectangle.java @@ -4,7 +4,7 @@ package com.example.geometry; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public final class FilledRectangle extends Rectangle { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Rectangle.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Rectangle.java similarity index 71% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Rectangle.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Rectangle.java index fd66cdd014..470c26d436 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Rectangle.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Rectangle.java @@ -4,7 +4,7 @@ package com.example.geometry; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public sealed class Rectangle extends Shape permits TransparentRectangle, FilledRectangle { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Shape.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.java similarity index 68% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Shape.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.java index cdec22ef4c..c6687dc3f6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Shape.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.java @@ -4,7 +4,7 @@ package com.example.geometry; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public sealed class Shape permits Circle, Rectangle, Square { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.txt new file mode 100644 index 0000000000..d296c77eb9 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Shape.txt @@ -0,0 +1,10 @@ ++- CompilationUnit[@PackageName = "com.example.geometry", @declarationsAreInDefaultPackage = "false"] + +- PackageDeclaration[@Name = "com.example.geometry"] + | +- ModifierList[] + +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "com.example.geometry.Shape", @CanonicalName = "com.example.geometry.Shape", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Image = "Shape", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "com.example.geometry", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "Shape", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "public", @Volatile = "false"] + +- ModifierList[] + +- PermitsList[@Size = "3"] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Circle", @TypeImage = "Circle"] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Rectangle", @TypeImage = "Rectangle"] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Square", @TypeImage = "Square"] + +- ClassOrInterfaceBody[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Square.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.java similarity index 66% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Square.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.java index 75dafdbc92..036d8d00e7 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/Square.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.java @@ -4,7 +4,7 @@ package com.example.geometry; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public non-sealed class Square extends Shape { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.txt new file mode 100644 index 0000000000..355c06b942 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/Square.txt @@ -0,0 +1,8 @@ ++- CompilationUnit[@PackageName = "com.example.geometry", @declarationsAreInDefaultPackage = "false"] + +- PackageDeclaration[@Name = "com.example.geometry"] + | +- ModifierList[] + +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "com.example.geometry.Square", @CanonicalName = "com.example.geometry.Square", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Image = "Square", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "com.example.geometry", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "Square", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "public", @Volatile = "false"] + +- ModifierList[] + +- ExtendsList[@Size = "1"] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Shape", @TypeImage = "Shape"] + +- ClassOrInterfaceBody[@Size = "0"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/TransparentRectangle.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/TransparentRectangle.java similarity index 67% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/TransparentRectangle.java rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/TransparentRectangle.java index a5c656157f..420ef3e792 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java15p/geometry/TransparentRectangle.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17/geometry/TransparentRectangle.java @@ -4,7 +4,7 @@ package com.example.geometry; /** - * @see JEP 360: Sealed Classes (Preview) + * @see JEP 409: Sealed Classes */ public final class TransparentRectangle extends Rectangle { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/DealingWithNull.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/DealingWithNull.java new file mode 100644 index 0000000000..4022f189e4 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/DealingWithNull.java @@ -0,0 +1,68 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +/** + * @see JEP 406: Pattern Matching for switch (Preview) + */ +public class DealingWithNull { + + + static void test(Object o) { + switch (o) { + case null -> System.out.println("null!"); + case String s -> System.out.println("String"); + default -> System.out.println("Something else"); + } + } + + + static void test2(Object o) { + switch (o) { + case null -> throw new NullPointerException(); + case String s -> System.out.println("String: "+s); + case Integer i -> System.out.println("Integer"); + default -> System.out.println("default"); + } + } + + + static void test3(Object o) { + switch(o) { + case null: case String s: + System.out.println("String, including null"); + break; + default: + System.out.println("default case"); + break; + } + + switch(o) { + case null, String s -> System.out.println("String, including null"); + default -> System.out.println("default case"); + } + + switch(o) { + case null: default: + System.out.println("The rest (including null)"); + } + + switch(o) { + case null, default -> + System.out.println("The rest (including null)"); + } + } + + public static void main(String[] args) { + test("test"); + test2(2); + try { + test2(null); + } catch (NullPointerException e) { + System.out.println(e); + } + test3(3); + test3("test"); + test3(null); + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/DealingWithNull.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/DealingWithNull.txt new file mode 100644 index 0000000000..9a93b59dde --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/DealingWithNull.txt @@ -0,0 +1,233 @@ ++- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = "true"] + +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "DealingWithNull", @CanonicalName = "DealingWithNull", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Image = "DealingWithNull", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "DealingWithNull", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "public", @Volatile = "false"] + +- ModifierList[] + +- ClassOrInterfaceBody[@Size = "4"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "package", @Final = "false", @Image = "test", @MainMethod = "false", @MethodName = "test", @Name = "test", @Native = "false", @Overridden = "false", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "package", @Void = "true", @Volatile = "false"] + | +- ModifierList[] + | +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + | +- FormalParameters[@Size = "1"] + | | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | | +- ModifierList[] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "o", @LambdaParameter = "false", @LocalVariable = "false", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | +- Block[@Size = "1", @containsComment = "false"] + | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "null!", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"null!\"", @IntLiteral = "false", @Length = "5", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "String", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"String\"", @IntLiteral = "false", @Length = "6", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchArrowBranch[@Default = "true"] + | +- SwitchLabel[@Default = "true"] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Something else", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Something else\"", @IntLiteral = "false", @Length = "14", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "package", @Final = "false", @Image = "test2", @MainMethod = "false", @MethodName = "test2", @Name = "test2", @Native = "false", @Overridden = "false", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "package", @Void = "true", @Volatile = "false"] + | +- ModifierList[] + | +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + | +- FormalParameters[@Size = "1"] + | | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | | +- ModifierList[] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "o", @LambdaParameter = "false", @LocalVariable = "false", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | +- Block[@Size = "1", @containsComment = "false"] + | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + | | +- ThrowStatement[] + | | +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "false", @Expression = "true", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "NullPointerException", @TypeImage = "NullPointerException"] + | | +- ArgumentList[@Size = "0"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "String: ", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"String: \"", @IntLiteral = "false", @Length = "8", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Integer", @TypeImage = "Integer"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "i", @LambdaParameter = "false", @LocalVariable = "false", @Name = "i", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "i", @Visibility = "local", @Volatile = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Integer", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Integer\"", @IntLiteral = "false", @Length = "7", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchArrowBranch[@Default = "true"] + | +- SwitchLabel[@Default = "true"] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "default", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"default\"", @IntLiteral = "false", @Length = "7", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "package", @Final = "false", @Image = "test3", @MainMethod = "false", @MethodName = "test3", @Name = "test3", @Native = "false", @Overridden = "false", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "package", @Void = "true", @Volatile = "false"] + | +- ModifierList[] + | +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + | +- FormalParameters[@Size = "1"] + | | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | | +- ModifierList[] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "o", @LambdaParameter = "false", @LocalVariable = "false", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | +- Block[@Size = "4", @containsComment = "false"] + | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- SwitchFallthroughBranch[@Default = "false"] + | | | +- SwitchLabel[@Default = "false"] + | | | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + | | +- SwitchFallthroughBranch[@Default = "false"] + | | | +- SwitchLabel[@Default = "false"] + | | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | | +- ExpressionStatement[] + | | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | | +- ArgumentList[@Size = "1"] + | | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "String, including null", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"String, including null\"", @IntLiteral = "false", @Length = "22", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | | +- BreakStatement[@Label = null] + | | +- SwitchFallthroughBranch[@Default = "true"] + | | +- SwitchLabel[@Default = "true"] + | | +- ExpressionStatement[] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "default case", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"default case\"", @IntLiteral = "false", @Length = "12", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- BreakStatement[@Label = null] + | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- SwitchArrowBranch[@Default = "false"] + | | | +- SwitchLabel[@Default = "false"] + | | | | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + | | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "String, including null", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"String, including null\"", @IntLiteral = "false", @Length = "22", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- SwitchArrowBranch[@Default = "true"] + | | +- SwitchLabel[@Default = "true"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "default case", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"default case\"", @IntLiteral = "false", @Length = "12", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- SwitchFallthroughBranch[@Default = "false"] + | | | +- SwitchLabel[@Default = "false"] + | | | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + | | +- SwitchFallthroughBranch[@Default = "true"] + | | +- SwitchLabel[@Default = "true"] + | | +- ExpressionStatement[] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "The rest (including null)", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"The rest (including null)\"", @IntLiteral = "false", @Length = "25", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "true"] + | +- SwitchLabel[@Default = "true"] + | | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "The rest (including null)", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"The rest (including null)\"", @IntLiteral = "false", @Length = "25", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "public", @Final = "false", @Image = "main", @MainMethod = "true", @MethodName = "main", @Name = "main", @Native = "false", @Overridden = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "public", @Void = "true", @Volatile = "false"] + +- ModifierList[] + +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + +- FormalParameters[@Size = "1"] + | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | +- ModifierList[] + | +- ArrayType[@ArrayDepth = "1", @ArrayType = "true", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "String[]"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | +- ArrayDimensions[@Size = "1"] + | | +- ArrayTypeDim[@Varargs = "false"] + | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "true", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "args", @LambdaParameter = "false", @LocalVariable = "false", @Name = "args", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "args", @Visibility = "local", @Volatile = "false"] + +- Block[@Size = "6", @containsComment = "false"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "test", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"test\"", @IntLiteral = "false", @Length = "4", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test2", @MethodName = "test2", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "2", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "2.0", @ValueAsFloat = "2.0", @ValueAsInt = "2", @ValueAsLong = "2"] + +- TryStatement[@TryWithResources = "false"] + | +- Block[@Size = "1", @containsComment = "false"] + | | +- ExpressionStatement[] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test2", @MethodName = "test2", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- ArgumentList[@Size = "1"] + | | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + | +- CatchClause[] + | +- CatchParameter[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Multicatch = "false", @Name = "e", @Native = "false", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | +- ModifierList[] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "NullPointerException", @TypeImage = "NullPointerException"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "package", @EnumConstant = "false", @ExceptionBlockParameter = "true", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "e", @LambdaParameter = "false", @LocalVariable = "false", @Name = "e", @Native = "false", @PackagePrivate = "true", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "e", @Visibility = "package", @Volatile = "false"] + | +- Block[@Size = "1", @containsComment = "false"] + | +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | +- ArgumentList[@Size = "1"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "e", @Name = "e", @ParenthesisDepth = "0", @Parenthesized = "false"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test3", @MethodName = "test3", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "3", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "3.0", @ValueAsFloat = "3.0", @ValueAsInt = "3", @ValueAsLong = "3"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test3", @MethodName = "test3", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "test", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"test\"", @IntLiteral = "false", @Length = "4", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- ExpressionStatement[] + +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test3", @MethodName = "test3", @ParenthesisDepth = "0", @Parenthesized = "false"] + +- ArgumentList[@Size = "1"] + +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/EnhancedTypeCheckingSwitch.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/EnhancedTypeCheckingSwitch.java new file mode 100644 index 0000000000..eb745cdfb8 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/EnhancedTypeCheckingSwitch.java @@ -0,0 +1,29 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +/** + * @see JEP 406: Pattern Matching for switch (Preview) + */ +public class EnhancedTypeCheckingSwitch { + + + static void typeTester(Object o) { + switch (o) { + case null -> System.out.println("null"); + case String s -> System.out.println("String"); + case Color c -> System.out.println("Color with " + c.values().length + " values"); + case Point p -> System.out.println("Record class: " + p.toString()); + case int[] ia -> System.out.println("Array of ints of length" + ia.length); + default -> System.out.println("Something else"); + } + } + + public static void main(String[] args) { + Object o = "test"; + typeTester(o); + } +} + +record Point(int i, int j) {} +enum Color { RED, GREEN, BLUE; } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/EnhancedTypeCheckingSwitch.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/EnhancedTypeCheckingSwitch.txt new file mode 100644 index 0000000000..bf6a1e1fae --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/EnhancedTypeCheckingSwitch.txt @@ -0,0 +1,143 @@ ++- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = "true"] + +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "EnhancedTypeCheckingSwitch", @CanonicalName = "EnhancedTypeCheckingSwitch", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Image = "EnhancedTypeCheckingSwitch", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "EnhancedTypeCheckingSwitch", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "public", @Volatile = "false"] + | +- ModifierList[] + | +- ClassOrInterfaceBody[@Size = "2"] + | +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "package", @Final = "false", @Image = "typeTester", @MainMethod = "false", @MethodName = "typeTester", @Name = "typeTester", @Native = "false", @Overridden = "false", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "package", @Void = "true", @Volatile = "false"] + | | +- ModifierList[] + | | +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + | | +- FormalParameters[@Size = "1"] + | | | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "o", @LambdaParameter = "false", @LocalVariable = "false", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | | +- Block[@Size = "1", @containsComment = "false"] + | | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- SwitchArrowBranch[@Default = "false"] + | | | +- SwitchLabel[@Default = "false"] + | | | | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "null", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"null\"", @IntLiteral = "false", @Length = "4", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- SwitchArrowBranch[@Default = "false"] + | | | +- SwitchLabel[@Default = "false"] + | | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "String", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"String\"", @IntLiteral = "false", @Length = "6", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- SwitchArrowBranch[@Default = "false"] + | | | +- SwitchLabel[@Default = "false"] + | | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Color", @TypeImage = "Color"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "c", @LambdaParameter = "false", @LocalVariable = "false", @Name = "c", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "c", @Visibility = "local", @Volatile = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Color with ", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Color with \"", @IntLiteral = "false", @Length = "11", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "length", @Name = "length", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "values", @MethodName = "values", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "c", @Name = "c", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "c"] + | | | | +- ArgumentList[@Size = "0"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = " values", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\" values\"", @IntLiteral = "false", @Length = "7", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- SwitchArrowBranch[@Default = "false"] + | | | +- SwitchLabel[@Default = "false"] + | | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Point", @TypeImage = "Point"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "p", @LambdaParameter = "false", @LocalVariable = "false", @Name = "p", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "p", @Visibility = "local", @Volatile = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Record class: ", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Record class: \"", @IntLiteral = "false", @Length = "14", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "toString", @MethodName = "toString", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "p", @Name = "p", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "p"] + | | | +- ArgumentList[@Size = "0"] + | | +- SwitchArrowBranch[@Default = "false"] + | | | +- SwitchLabel[@Default = "false"] + | | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ArrayType[@ArrayDepth = "1", @ArrayType = "true", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "int[]"] + | | | | | +- PrimitiveType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @Kind = "int", @PrimitiveType = "true", @TypeImage = "int"] + | | | | | +- ArrayDimensions[@Size = "1"] + | | | | | +- ArrayTypeDim[@Varargs = "false"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "true", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "ia", @LambdaParameter = "false", @LocalVariable = "false", @Name = "ia", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "ia", @Visibility = "local", @Volatile = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Array of ints of length", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Array of ints of length\"", @IntLiteral = "false", @Length = "23", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "length", @Name = "length", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "ia", @Name = "ia", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "ia"] + | | +- SwitchArrowBranch[@Default = "true"] + | | +- SwitchLabel[@Default = "true"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Something else", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Something else\"", @IntLiteral = "false", @Length = "14", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "public", @Final = "false", @Image = "main", @MainMethod = "true", @MethodName = "main", @Name = "main", @Native = "false", @Overridden = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "public", @Void = "true", @Volatile = "false"] + | +- ModifierList[] + | +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + | +- FormalParameters[@Size = "1"] + | | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | | +- ModifierList[] + | | +- ArrayType[@ArrayDepth = "1", @ArrayType = "true", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "String[]"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | +- ArrayDimensions[@Size = "1"] + | | | +- ArrayTypeDim[@Varargs = "false"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "true", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "args", @LambdaParameter = "false", @LocalVariable = "false", @Name = "args", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "args", @Visibility = "local", @Volatile = "false"] + | +- Block[@Size = "2", @containsComment = "false"] + | +- LocalVariableDeclaration[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @Visibility = "local", @Volatile = "false"] + | | +- ModifierList[] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | | +- VariableDeclarator[@Initializer = "true", @Name = "o"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "o", @LambdaParameter = "false", @LocalVariable = "true", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "test", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"test\"", @IntLiteral = "false", @Length = "4", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "typeTester", @MethodName = "typeTester", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + +- RecordDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Point", @CanonicalName = "Point", @EffectiveVisibility = "package", @Enum = "false", @Final = "true", @Image = "Point", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Record = "true", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "Point", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | +- ModifierList[] + | +- RecordComponentList[@Size = "2", @Varargs = "false"] + | | +- RecordComponent[@Abstract = "false", @EffectiveVisibility = "private", @Final = "true", @Native = "false", @PackagePrivate = "false", @Private = "true", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "private", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- PrimitiveType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @Kind = "int", @PrimitiveType = "true", @TypeImage = "int"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "i", @LambdaParameter = "false", @LocalVariable = "false", @Name = "i", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "true", @Protected = "false", @Public = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "i", @Visibility = "private", @Volatile = "false"] + | | +- RecordComponent[@Abstract = "false", @EffectiveVisibility = "private", @Final = "true", @Native = "false", @PackagePrivate = "false", @Private = "true", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "private", @Volatile = "false"] + | | +- ModifierList[] + | | +- PrimitiveType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @Kind = "int", @PrimitiveType = "true", @TypeImage = "int"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "private", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "j", @LambdaParameter = "false", @LocalVariable = "false", @Name = "j", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "true", @Protected = "false", @Public = "false", @RecordComponent = "true", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "j", @Visibility = "private", @Volatile = "false"] + | +- RecordBody[@Size = "0"] + +- EnumDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "Color", @CanonicalName = "Color", @EffectiveVisibility = "package", @Enum = "true", @Final = "true", @Image = "Color", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Record = "false", @RegularClass = "false", @RegularInterface = "false", @SimpleName = "Color", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "package", @Volatile = "false"] + +- ModifierList[] + +- EnumBody[@SeparatorSemi = "true", @Size = "3", @TrailingComma = "false"] + +- EnumConstant[@Abstract = "false", @AnonymousClass = "false", @EffectiveVisibility = "package", @Final = "true", @Image = "RED", @MethodName = "new", @Name = "RED", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "public", @Volatile = "false"] + | +- ModifierList[] + | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "package", @EnumConstant = "true", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "RED", @LambdaParameter = "false", @LocalVariable = "false", @Name = "RED", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "true", @VariableName = "RED", @Visibility = "public", @Volatile = "false"] + +- EnumConstant[@Abstract = "false", @AnonymousClass = "false", @EffectiveVisibility = "package", @Final = "true", @Image = "GREEN", @MethodName = "new", @Name = "GREEN", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "public", @Volatile = "false"] + | +- ModifierList[] + | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "package", @EnumConstant = "true", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "GREEN", @LambdaParameter = "false", @LocalVariable = "false", @Name = "GREEN", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "true", @VariableName = "GREEN", @Visibility = "public", @Volatile = "false"] + +- EnumConstant[@Abstract = "false", @AnonymousClass = "false", @EffectiveVisibility = "package", @Final = "true", @Image = "BLUE", @MethodName = "new", @Name = "BLUE", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "public", @Volatile = "false"] + +- ModifierList[] + +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "package", @EnumConstant = "true", @ExceptionBlockParameter = "false", @Field = "false", @Final = "true", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "BLUE", @LambdaParameter = "false", @LocalVariable = "false", @Name = "BLUE", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "true", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "true", @VariableName = "BLUE", @Visibility = "public", @Volatile = "false"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.java new file mode 100644 index 0000000000..fd4c1d58d5 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.java @@ -0,0 +1,42 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +/** + * @see JEP 406: Pattern Matching for switch (Preview) + */ +public class GuardedAndParenthesizedPatterns { + + + static void test(Object o) { + switch (o) { + case String s && (s.length() == 1) -> System.out.println("single char string"); + case String s -> System.out.println("string"); + case (Integer i && i.intValue() == 1) -> System.out.println("integer 1"); + case (((Long l && l.longValue() == 1L))) -> System.out.println("long 1 with parens"); + case (((Double d))) -> System.out.println("double with parens"); + default -> System.out.println("default case"); + } + } + + static void instanceOfPattern(Object o) { + if (o instanceof String s && s.length() > 2) { + System.out.println("A string containing at least two characters"); + } + if (o != null && (o instanceof String s && s.length() > 3)) { + System.out.println("A string containing at least three characters"); + } + if (o instanceof (String s && s.length() > 4)) { + System.out.println("A string containing at least four characters"); + } + } + + public static void main(String[] args) { + test("a"); + test("fooo"); + test(1); + test(1L); + instanceOfPattern("abcde"); + test(null); + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.txt new file mode 100644 index 0000000000..353c8e05ed --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/GuardedAndParenthesizedPatterns.txt @@ -0,0 +1,217 @@ ++- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = "true"] + +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "GuardedAndParenthesizedPatterns", @CanonicalName = "GuardedAndParenthesizedPatterns", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Image = "GuardedAndParenthesizedPatterns", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "GuardedAndParenthesizedPatterns", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "public", @Volatile = "false"] + +- ModifierList[] + +- ClassOrInterfaceBody[@Size = "3"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "package", @Final = "false", @Image = "test", @MainMethod = "false", @MethodName = "test", @Name = "test", @Native = "false", @Overridden = "false", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "package", @Void = "true", @Volatile = "false"] + | +- ModifierList[] + | +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + | +- FormalParameters[@Size = "1"] + | | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | | +- ModifierList[] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "o", @LambdaParameter = "false", @LocalVariable = "false", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | +- Block[@Size = "1", @containsComment = "false"] + | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- GuardedPattern[@ParenthesisDepth = "0"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "==", @ParenthesisDepth = "1", @Parenthesized = "true"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "length", @MethodName = "length", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "s"] + | | | | +- ArgumentList[@Size = "0"] + | | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "1", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "1.0", @ValueAsFloat = "1.0", @ValueAsInt = "1", @ValueAsLong = "1"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "single char string", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"single char string\"", @IntLiteral = "false", @Length = "18", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "string", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"string\"", @IntLiteral = "false", @Length = "6", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- GuardedPattern[@ParenthesisDepth = "1"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Integer", @TypeImage = "Integer"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "i", @LambdaParameter = "false", @LocalVariable = "false", @Name = "i", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "i", @Visibility = "local", @Volatile = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "==", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "intValue", @MethodName = "intValue", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "i", @Name = "i", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "i"] + | | | | +- ArgumentList[@Size = "0"] + | | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "1", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "1.0", @ValueAsFloat = "1.0", @ValueAsInt = "1", @ValueAsLong = "1"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "integer 1", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"integer 1\"", @IntLiteral = "false", @Length = "9", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- GuardedPattern[@ParenthesisDepth = "3"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Long", @TypeImage = "Long"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "l", @LambdaParameter = "false", @LocalVariable = "false", @Name = "l", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "l", @Visibility = "local", @Volatile = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "==", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "longValue", @MethodName = "longValue", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "l", @Name = "l", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "l"] + | | | | +- ArgumentList[@Size = "0"] + | | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "1L", @IntLiteral = "false", @Integral = "true", @LongLiteral = "true", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "1.0", @ValueAsFloat = "1.0", @ValueAsInt = "1", @ValueAsLong = "1"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "long 1 with parens", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"long 1 with parens\"", @IntLiteral = "false", @Length = "18", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "3", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Double", @TypeImage = "Double"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "d", @LambdaParameter = "false", @LocalVariable = "false", @Name = "d", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "d", @Visibility = "local", @Volatile = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "double with parens", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"double with parens\"", @IntLiteral = "false", @Length = "18", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchArrowBranch[@Default = "true"] + | +- SwitchLabel[@Default = "true"] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "default case", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"default case\"", @IntLiteral = "false", @Length = "12", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "package", @Final = "false", @Image = "instanceOfPattern", @MainMethod = "false", @MethodName = "instanceOfPattern", @Name = "instanceOfPattern", @Native = "false", @Overridden = "false", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "package", @Void = "true", @Volatile = "false"] + | +- ModifierList[] + | +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + | +- FormalParameters[@Size = "1"] + | | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | | +- ModifierList[] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "o", @LambdaParameter = "false", @LocalVariable = "false", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | +- Block[@Size = "3", @containsComment = "false"] + | +- IfStatement[@Else = "false"] + | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "&&", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- PatternExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "length", @MethodName = "length", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "s"] + | | | | +- ArgumentList[@Size = "0"] + | | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "2", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "2.0", @ValueAsFloat = "2.0", @ValueAsInt = "2", @ValueAsLong = "2"] + | | +- Block[@Size = "1", @containsComment = "false"] + | | +- ExpressionStatement[] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "A string containing at least two characters", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"A string containing at least two characters\"", @IntLiteral = "false", @Length = "43", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- IfStatement[@Else = "false"] + | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "&&", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "!=", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "&&", @ParenthesisDepth = "1", @Parenthesized = "true"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- PatternExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | | +- ModifierList[] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "length", @MethodName = "length", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "s"] + | | | | +- ArgumentList[@Size = "0"] + | | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "3", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "3.0", @ValueAsFloat = "3.0", @ValueAsInt = "3", @ValueAsLong = "3"] + | | +- Block[@Size = "1", @containsComment = "false"] + | | +- ExpressionStatement[] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "A string containing at least three characters", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"A string containing at least three characters\"", @IntLiteral = "false", @Length = "45", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- IfStatement[@Else = "false"] + | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "instanceof", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- PatternExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- GuardedPattern[@ParenthesisDepth = "1"] + | | +- ModifierList[] + | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "length", @MethodName = "length", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "s"] + | | | +- ArgumentList[@Size = "0"] + | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "4", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "4.0", @ValueAsFloat = "4.0", @ValueAsInt = "4", @ValueAsLong = "4"] + | +- Block[@Size = "1", @containsComment = "false"] + | +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "A string containing at least four characters", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"A string containing at least four characters\"", @IntLiteral = "false", @Length = "44", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "public", @Final = "false", @Image = "main", @MainMethod = "true", @MethodName = "main", @Name = "main", @Native = "false", @Overridden = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "public", @Void = "true", @Volatile = "false"] + +- ModifierList[] + +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + +- FormalParameters[@Size = "1"] + | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | +- ModifierList[] + | +- ArrayType[@ArrayDepth = "1", @ArrayType = "true", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "String[]"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | +- ArrayDimensions[@Size = "1"] + | | +- ArrayTypeDim[@Varargs = "false"] + | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "true", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "args", @LambdaParameter = "false", @LocalVariable = "false", @Name = "args", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "args", @Visibility = "local", @Volatile = "false"] + +- Block[@Size = "6", @containsComment = "false"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "a", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"a\"", @IntLiteral = "false", @Length = "1", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "fooo", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"fooo\"", @IntLiteral = "false", @Length = "4", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "1", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "1.0", @ValueAsFloat = "1.0", @ValueAsInt = "1", @ValueAsLong = "1"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "1L", @IntLiteral = "false", @Integral = "true", @LongLiteral = "true", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "1.0", @ValueAsFloat = "1.0", @ValueAsInt = "1", @ValueAsLong = "1"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "instanceOfPattern", @MethodName = "instanceOfPattern", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "abcde", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"abcde\"", @IntLiteral = "false", @Length = "5", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + +- ExpressionStatement[] + +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] + +- ArgumentList[@Size = "1"] + +- NullLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "false", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "true", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/PatternsInSwitchLabels.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/PatternsInSwitchLabels.java new file mode 100644 index 0000000000..ce4427b372 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/PatternsInSwitchLabels.java @@ -0,0 +1,22 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +/** + * @see JEP 406: Pattern Matching for switch (Preview) + */ +public class PatternsInSwitchLabels { + + + public static void main(String[] args) { + Object o = 123L; + String formatted = switch (o) { + case Integer i -> String.format("int %d", i); + case Long l -> String.format("long %d", l); + case Double d -> String.format("double %f", d); + case String s -> String.format("String %s", s); + default -> o.toString(); + }; + System.out.println(formatted); + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/PatternsInSwitchLabels.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/PatternsInSwitchLabels.txt new file mode 100644 index 0000000000..f888eaf57a --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/PatternsInSwitchLabels.txt @@ -0,0 +1,89 @@ ++- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = "true"] + +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "PatternsInSwitchLabels", @CanonicalName = "PatternsInSwitchLabels", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Image = "PatternsInSwitchLabels", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "PatternsInSwitchLabels", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "public", @Volatile = "false"] + +- ModifierList[] + +- ClassOrInterfaceBody[@Size = "1"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "public", @Final = "false", @Image = "main", @MainMethod = "true", @MethodName = "main", @Name = "main", @Native = "false", @Overridden = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "public", @Void = "true", @Volatile = "false"] + +- ModifierList[] + +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + +- FormalParameters[@Size = "1"] + | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | +- ModifierList[] + | +- ArrayType[@ArrayDepth = "1", @ArrayType = "true", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "String[]"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | +- ArrayDimensions[@Size = "1"] + | | +- ArrayTypeDim[@Varargs = "false"] + | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "true", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "args", @LambdaParameter = "false", @LocalVariable = "false", @Name = "args", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "args", @Visibility = "local", @Volatile = "false"] + +- Block[@Size = "3", @containsComment = "false"] + +- LocalVariableDeclaration[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @Visibility = "local", @Volatile = "false"] + | +- ModifierList[] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | +- VariableDeclarator[@Initializer = "true", @Name = "o"] + | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "o", @LambdaParameter = "false", @LocalVariable = "true", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "123L", @IntLiteral = "false", @Integral = "true", @LongLiteral = "true", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "123.0", @ValueAsFloat = "123.0", @ValueAsInt = "123", @ValueAsLong = "123"] + +- LocalVariableDeclaration[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @Visibility = "local", @Volatile = "false"] + | +- ModifierList[] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | +- VariableDeclarator[@Initializer = "true", @Name = "formatted"] + | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "formatted", @LambdaParameter = "false", @LocalVariable = "true", @Name = "formatted", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "formatted", @Visibility = "local", @Volatile = "false"] + | +- SwitchExpression[@CompileTimeConstant = "false", @DefaultCase = "true", @ExhaustiveEnumSwitch = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Integer", @TypeImage = "Integer"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "i", @LambdaParameter = "false", @LocalVariable = "false", @Name = "i", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "i", @Visibility = "local", @Volatile = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "format", @MethodName = "format", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | +- ArgumentList[@Size = "2"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "int %d", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"int %d\"", @IntLiteral = "false", @Length = "6", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "i", @Name = "i", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Long", @TypeImage = "Long"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "l", @LambdaParameter = "false", @LocalVariable = "false", @Name = "l", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "l", @Visibility = "local", @Volatile = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "format", @MethodName = "format", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | +- ArgumentList[@Size = "2"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "long %d", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"long %d\"", @IntLiteral = "false", @Length = "7", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "l", @Name = "l", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Double", @TypeImage = "Double"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "d", @LambdaParameter = "false", @LocalVariable = "false", @Name = "d", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "d", @Visibility = "local", @Volatile = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "format", @MethodName = "format", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | +- ArgumentList[@Size = "2"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "double %f", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"double %f\"", @IntLiteral = "false", @Length = "9", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "d", @Name = "d", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "format", @MethodName = "format", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | +- ArgumentList[@Size = "2"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "String %s", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"String %s\"", @IntLiteral = "false", @Length = "9", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "true"] + | +- SwitchLabel[@Default = "true"] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "toString", @MethodName = "toString", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "0"] + +- ExpressionStatement[] + +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + +- ArgumentList[@Size = "1"] + +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "formatted", @Name = "formatted", @ParenthesisDepth = "0", @Parenthesized = "false"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/ScopeOfPatternVariableDeclarations.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/ScopeOfPatternVariableDeclarations.java new file mode 100644 index 0000000000..f0caa28558 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/ScopeOfPatternVariableDeclarations.java @@ -0,0 +1,48 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +/** + * @see JEP 406: Pattern Matching for switch (Preview) + */ +public class ScopeOfPatternVariableDeclarations { + + + static void test(Object o) { + switch (o) { + case Character c -> { + if (c.charValue() == 7) { + System.out.println("Ding!"); + } + System.out.println("Character"); + } + case Integer i -> + throw new IllegalStateException("Invalid Integer argument of value " + i.intValue()); + default -> { + break; + } + } + } + + + static void test2(Object o) { + switch (o) { + case Character c: + if (c.charValue() == 7) { + System.out.print("Ding "); + } + if (c.charValue() == 9) { + System.out.print("Tab "); + } + System.out.println("character"); + default: + System.out.println(); + } + } + + + public static void main(String[] args) { + test('A'); + test2('\t'); + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/ScopeOfPatternVariableDeclarations.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/ScopeOfPatternVariableDeclarations.txt new file mode 100644 index 0000000000..325c6a40f3 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/java17p/ScopeOfPatternVariableDeclarations.txt @@ -0,0 +1,142 @@ ++- CompilationUnit[@PackageName = "", @declarationsAreInDefaultPackage = "true"] + +- ClassOrInterfaceDeclaration[@Abstract = "false", @Annotation = "false", @Anonymous = "false", @BinaryName = "ScopeOfPatternVariableDeclarations", @CanonicalName = "ScopeOfPatternVariableDeclarations", @EffectiveVisibility = "public", @Enum = "false", @Final = "false", @Image = "ScopeOfPatternVariableDeclarations", @Interface = "false", @Local = "false", @Native = "false", @Nested = "false", @PackageName = "", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Record = "false", @RegularClass = "true", @RegularInterface = "false", @SimpleName = "ScopeOfPatternVariableDeclarations", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "false", @TopLevel = "true", @Transient = "false", @Visibility = "public", @Volatile = "false"] + +- ModifierList[] + +- ClassOrInterfaceBody[@Size = "3"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "package", @Final = "false", @Image = "test", @MainMethod = "false", @MethodName = "test", @Name = "test", @Native = "false", @Overridden = "false", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "package", @Void = "true", @Volatile = "false"] + | +- ModifierList[] + | +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + | +- FormalParameters[@Size = "1"] + | | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | | +- ModifierList[] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "o", @LambdaParameter = "false", @LocalVariable = "false", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | +- Block[@Size = "1", @containsComment = "false"] + | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Character", @TypeImage = "Character"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "c", @LambdaParameter = "false", @LocalVariable = "false", @Name = "c", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "c", @Visibility = "local", @Volatile = "false"] + | | +- Block[@Size = "2", @containsComment = "false"] + | | +- IfStatement[@Else = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "==", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "charValue", @MethodName = "charValue", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "c", @Name = "c", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "c"] + | | | | | +- ArgumentList[@Size = "0"] + | | | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "7", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "7.0", @ValueAsFloat = "7.0", @ValueAsInt = "7", @ValueAsLong = "7"] + | | | +- Block[@Size = "1", @containsComment = "false"] + | | | +- ExpressionStatement[] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Ding!", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Ding!\"", @IntLiteral = "false", @Length = "5", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- ExpressionStatement[] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Character", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Character\"", @IntLiteral = "false", @Length = "9", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchArrowBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Integer", @TypeImage = "Integer"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "i", @LambdaParameter = "false", @LocalVariable = "false", @Name = "i", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "i", @Visibility = "local", @Volatile = "false"] + | | +- ThrowStatement[] + | | +- ConstructorCall[@AnonymousClass = "false", @CompileTimeConstant = "false", @DiamondTypeArgs = "false", @Expression = "true", @MethodName = "new", @ParenthesisDepth = "0", @Parenthesized = "false", @QualifiedInstanceCreation = "false"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "IllegalStateException", @TypeImage = "IllegalStateException"] + | | +- ArgumentList[@Size = "1"] + | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "+", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Invalid Integer argument of value ", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Invalid Integer argument of value \"", @IntLiteral = "false", @Length = "34", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "intValue", @MethodName = "intValue", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "i", @Name = "i", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "i"] + | | +- ArgumentList[@Size = "0"] + | +- SwitchArrowBranch[@Default = "true"] + | +- SwitchLabel[@Default = "true"] + | +- Block[@Size = "1", @containsComment = "false"] + | +- BreakStatement[@Label = null] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "package", @Final = "false", @Image = "test2", @MainMethod = "false", @MethodName = "test2", @Name = "test2", @Native = "false", @Overridden = "false", @PackagePrivate = "true", @Private = "false", @Protected = "false", @Public = "false", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "package", @Void = "true", @Volatile = "false"] + | +- ModifierList[] + | +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + | +- FormalParameters[@Size = "1"] + | | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | | +- ModifierList[] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Object", @TypeImage = "Object"] + | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "o", @LambdaParameter = "false", @LocalVariable = "false", @Name = "o", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "o", @Visibility = "local", @Volatile = "false"] + | +- Block[@Size = "1", @containsComment = "false"] + | +- SwitchStatement[@DefaultCase = "true", @ExhaustiveEnumSwitch = "false"] + | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- SwitchFallthroughBranch[@Default = "false"] + | | +- SwitchLabel[@Default = "false"] + | | | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"] + | | | +- ModifierList[] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "Character", @TypeImage = "Character"] + | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "c", @LambdaParameter = "false", @LocalVariable = "false", @Name = "c", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "c", @Visibility = "local", @Volatile = "false"] + | | +- IfStatement[@Else = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "==", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "charValue", @MethodName = "charValue", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "c", @Name = "c", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "c"] + | | | | | +- ArgumentList[@Size = "0"] + | | | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "7", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "7.0", @ValueAsFloat = "7.0", @ValueAsInt = "7", @ValueAsLong = "7"] + | | | +- Block[@Size = "1", @containsComment = "false"] + | | | +- ExpressionStatement[] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "print", @MethodName = "print", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Ding ", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Ding \"", @IntLiteral = "false", @Length = "5", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- IfStatement[@Else = "false"] + | | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = "==", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "charValue", @MethodName = "charValue", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "c", @Name = "c", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "c"] + | | | | | +- ArgumentList[@Size = "0"] + | | | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "9", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "9.0", @ValueAsFloat = "9.0", @ValueAsInt = "9", @ValueAsLong = "9"] + | | | +- Block[@Size = "1", @containsComment = "false"] + | | | +- ExpressionStatement[] + | | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "print", @MethodName = "print", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | | +- ArgumentList[@Size = "1"] + | | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "Tab ", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"Tab \"", @IntLiteral = "false", @Length = "4", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | | +- ExpressionStatement[] + | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | | +- ArgumentList[@Size = "1"] + | | +- StringLiteral[@BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @ConstValue = "character", @DoubleLiteral = "false", @Empty = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\"character\"", @IntLiteral = "false", @Length = "9", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "true", @TextBlock = "false"] + | +- SwitchFallthroughBranch[@Default = "true"] + | +- SwitchLabel[@Default = "true"] + | +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "println", @MethodName = "println", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- FieldAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "out", @Name = "out", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- TypeExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "System", @TypeImage = "System"] + | +- ArgumentList[@Size = "0"] + +- MethodDeclaration[@Abstract = "false", @Arity = "1", @EffectiveVisibility = "public", @Final = "false", @Image = "main", @MainMethod = "true", @MethodName = "main", @Name = "main", @Native = "false", @Overridden = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "true", @Static = "true", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "true", @SyntacticallyStatic = "true", @Transient = "false", @Varargs = "false", @Visibility = "public", @Void = "true", @Volatile = "false"] + +- ModifierList[] + +- VoidType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "void"] + +- FormalParameters[@Size = "1"] + | +- FormalParameter[@Abstract = "false", @EffectiveVisibility = "local", @Final = "false", @Native = "false", @PackagePrivate = "false", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Varargs = "false", @Visibility = "local", @Volatile = "false"] + | +- ModifierList[] + | +- ArrayType[@ArrayDepth = "1", @ArrayType = "true", @ClassOrInterfaceType = "false", @PrimitiveType = "false", @TypeImage = "String[]"] + | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"] + | | +- ArrayDimensions[@Size = "1"] + | | +- ArrayTypeDim[@Varargs = "false"] + | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "true", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "true", @Image = "args", @LambdaParameter = "false", @LocalVariable = "false", @Name = "args", @Native = "false", @PackagePrivate = "false", @PatternBinding = "false", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "args", @Visibility = "local", @Volatile = "false"] + +- Block[@Size = "2", @containsComment = "false"] + +- ExpressionStatement[] + | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test", @MethodName = "test", @ParenthesisDepth = "0", @Parenthesized = "false"] + | +- ArgumentList[@Size = "1"] + | +- CharLiteral[@BooleanLiteral = "false", @CharLiteral = "true", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\'A\'", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] + +- ExpressionStatement[] + +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "test2", @MethodName = "test2", @ParenthesisDepth = "0", @Parenthesized = "false"] + +- ArgumentList[@Size = "1"] + +- CharLiteral[@BooleanLiteral = "false", @CharLiteral = "true", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "\'\\t\'", @IntLiteral = "false", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "false", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false"] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.java index 497591db2c..8e3780ff45 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.java @@ -2,7 +2,7 @@ public class Foo { public void bar() { System.out.println("hello"); System.out.println("hello"); - int i = 5 + int i = 5; System.out.print("hello"); } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations.java index b581a387f0..6a1bb6b24f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations.java @@ -10,4 +10,4 @@ package foo.bar.baz; public class Foo {} @SuppressWarnings({"ugh","CPD-END"}) - +class Other {} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations.txt index 938bf8fb9a..0375f93fd6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations.txt @@ -10,4 +10,9 @@ L12 ["CPD-END"] 26 35 [}] 35 36 [)] 36 37 +L13 + [class] 1 6 + [Other] 7 12 + [{] 13 14 + [}] 14 15 EOF diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations_ignore_annots.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations_ignore_annots.txt index 49ab402048..c3fde40d3d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations_ignore_annots.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations_ignore_annots.txt @@ -1,2 +1,7 @@ [Image] or [Truncated image[ Bcol Ecol +L13 + [class] 1 5 + [Other] 7 11 + [{] 13 13 + [}] 14 14 EOF diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/tabWidth.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/tabWidth.java index 1e76e996b9..8dfe5729a5 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/tabWidth.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/tabWidth.java @@ -1 +1,3 @@ +class tabWidth { int i = 0; +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/tabWidth.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/tabWidth.txt index 4b234e2932..b606eb0db6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/tabWidth.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/tabWidth.txt @@ -1,7 +1,13 @@ [Image] or [Truncated image[ Bcol Ecol L1 + [class] 1 6 + [tabWidth] 7 15 + [{] 16 17 +L2 [int] 2 5 [i] 6 7 [=] 8 9 [0] 10 11 +L3 + [}] 1 2 EOF diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml index d3fde8ab50..0fedca0ac3 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml @@ -264,6 +264,7 @@ class Outer { ]]> java 10 + AccessorMethodGeneration false positive with overloads #807 0 @@ -310,6 +311,7 @@ class Outer { ]]> java 10 + AccessorMethodGeneration: Name clash with another public field not properly handled #342 0 @@ -337,4 +339,26 @@ class Outer { ]]> java 10 + + + nesting three levels + 1 + 3 + + java 10 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/ArrayIsStoredDirectly.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/ArrayIsStoredDirectly.xml index 084f650944..1e0cc438e8 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/ArrayIsStoredDirectly.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/ArrayIsStoredDirectly.xml @@ -235,6 +235,37 @@ public class AISD { public AISD of(final byte[] buf3) { return new AISD(buf3.clone()); } +} + ]]> + + + + nested classes + 4 + 4,9,16,20 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml index 6a2915da3f..7081b82c70 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml @@ -73,6 +73,7 @@ public class Foo extends TestCase { assertEquals bad 1 + 4 + + assertEquals bad, without import + 1 + 3 + + + assertEquals with delta bad 1 @@ -341,6 +355,9 @@ public class Foo { public void test1() { assertEquals(1, 1); } + private void assertEquals(int x, int y) { + if (x != y) throw new AssertionError(); + } } ]]> @@ -348,6 +365,7 @@ public class Foo { JUnit 4 - assertEquals 1 + 6 + + JUnit 4 - assertEquals - without imports + 1 + 4 + + + #1374 JUnitAssertionsShouldIncludeMessage does not work 4 @@ -436,6 +470,7 @@ class SimpleTest { } ]]> + [java] JUnitAssertionsShouldIncludeMessage false positive with method call #2883 0 @@ -453,6 +488,7 @@ public class AssertionMessageTest { } ]]> + JUnitAssertionsShouldIncludeMessage false positive with AssertJ #1565 0 @@ -466,7 +502,30 @@ public class AssertionMessageTest { .hasSize(3) .containsEntry(3, 4); } +} + ]]> + + + Assert calls should be considered in the whole file that contains at least one test case + 1 + 14 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml index 4d9c84f817..649bebe0c0 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml @@ -682,6 +682,26 @@ class JUnit5Test { void check() { Assertions.assertNotNull("FOO"); } +} + ]]> + + + + nested test classes + 1 + 8 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LooseCoupling.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LooseCoupling.xml index f9c456f8d2..aae78d63b1 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LooseCoupling.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LooseCoupling.xml @@ -7,6 +7,7 @@ returning a HashSet, bad 1 + 3 + + + Verify with nested (local) classes + 3 + 6,11,15 + + + + + False positive with generics #3672 + 0 + > { + private final Set things; } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MethodReturnsInternalArray.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MethodReturnsInternalArray.xml index 5dff4e3ecb..74cfe5bde3 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MethodReturnsInternalArray.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MethodReturnsInternalArray.xml @@ -260,7 +260,7 @@ public class MethodReturnsInternalArrayCase { - #1738 MethodReturnsInternalArray in inner classes + #1738 MethodReturnsInternalArray in inner classes 1 + + + + nested class in interface + 1 + 4 + + + + + [java] MethodReturnsInternalArray doesn't consider anonymous classes #3630 + 1 + 7 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml index 3f83a8e032..83e9ace29d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml @@ -189,10 +189,10 @@ public class ConcreteClassArrayParams extends AbstractClass { ]]> - - Consider enum anon class - 1 - 10 + + Consider enum anon class (#3542) + 2 + 6,11 java 16 + Package private method cannot be overridden outside of its package #1969 (1) 1 @@ -568,6 +567,7 @@ public record Point(int x, int y) { } ]]> + Package private method cannot be overridden outside of its package #1969 (2) 0 @@ -585,4 +585,71 @@ public record Point(int x, int y) { } ]]> + + + [java] MissingOverride: False negative for enum method #3542 + 3 + 6,11,17 + + + + + False positive with generic erasure #3675 + 1 + 23 + , T> { + @SuppressWarnings("unchecked") + public B defaultValue(T val) { + return (B) this; + } + + @SuppressWarnings("unchecked") + public B defaultValue2(T val) { + return (B) this; + } + + public static final class ConcreteBuilder> extends AbstractBuilderMixedTypeVarOverride, C> { + //@Override is wrong here: method does not override or implement a method from a supertype + public ConcreteBuilder defaultValue(Collection val) { + return this; + } + + //@Override is indeed missing here + public ConcreteBuilder defaultValue2(C val) { + return this; + } + } +} + ]]> + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PrimitiveWrapperInstantiation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PrimitiveWrapperInstantiation.xml new file mode 100644 index 0000000000..cc56bcfbb2 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PrimitiveWrapperInstantiation.xml @@ -0,0 +1,420 @@ + + + + + simple failure case + 2 + 2,3 + + Do not use `new Boolean("true")`, prefer `Boolean.TRUE` + Do not use `new Boolean("false")`, prefer `Boolean.FALSE` + + + + + + new java.lang.Boolean + 1 + 2 + + Do not use `new Boolean("true")`, prefer `Boolean.TRUE` + + + + + + ok with Boolean.TRUE constant + 0 + + + + + Boolean.valueOf - not constructors + 2 + 2,3 + + Do not use `Boolean.valueOf(true)`, prefer `Boolean.TRUE` + Do not use `Boolean.valueOf(false)`, prefer `Boolean.FALSE` + + + + + + Boolean.valueOf() with variable is fine + 0 + + + + + don't use Boolean.valueOf() with string literal + 1 + 2 + + Do not use `Boolean.valueOf("true")`, prefer `Boolean.TRUE` + + + + + + don't use new Boolean() in method call + 1 + 3 + + Do not use `new Boolean("true")`, prefer `Boolean.TRUE` + + + + + + don't use Boolean.valueOf() in method call + 1 + 3 + + + + + ok, Boolean.TRUE and Boolean.FALSE as method arguments + 0 + + + + + don't use Boolean.valueOf() or new Boolean() in static block + 2 + 3,4 + + + + + Bug 1744065, should be ok + 0 + + + + + Test for failure after rule with custom Boolean, should report failure if rule reset done correctly + 1 + 2 + + + + + #1533 [java] BooleanInstantiation: ClassCastException with Annotation + 0 + + + + + Arrays are ok + 0 + + + + + + new Short(), bad + 1 + 3 + + Do not use `new Short(...)`, prefer `Short.valueOf(...)` + + + + + + Short.valueOf(), ok + 0 + + + + + + new Integer(), bad + 1 + 2 + + Do not use `new Integer(...)`, prefer `Integer.valueOf(...)` + + + + + + Integer.valueOf(), ok + 0 + + + + + + new Long(), bad + 1 + 3 + + Do not use `new Long(...)`, prefer `Long.valueOf(...)` + + + + + + Long.valueOf(), ok + 0 + + + + + + new Double(), bad + 1 + 3 + + Do not use `new Double(...)`, prefer `Double.valueOf(...)` + + + + + + Double.valueOf(), ok + 0 + + + + + + new Float(), bad + 1 + 3 + + Do not use `new Float(...)`, prefer `Float.valueOf(...)` + + + + + + Float.valueOf(), ok + 0 + + + + + + new Byte(), bad + 1 + 3 + + Do not use `new Byte(...)`, prefer `Byte.valueOf(...)` + + + + + + Byte.valueOf(), ok + 0 + + + + + + new Character(), bad + 1 + 3 + + Do not use `new Character(...)`, prefer `Character.valueOf(...)` + + + + + + Character.valueOf(), ok + 0 + + + + + [java] PrimitiveWrapperInstantiation: no violation on 'new Boolean(val)' #3595 + 2 + 5,6 + + Do not use `new Boolean(...)`, prefer `Boolean.valueOf` + Do not use `new Boolean("...")`, prefer `Boolean.valueOf` + + + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SimplifiableTestAssertion.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SimplifiableTestAssertion.xml new file mode 100644 index 0000000000..85966d420b --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SimplifiableTestAssertion.xml @@ -0,0 +1,662 @@ + + + + + UseAssertNullInsteadOfAssertTrue: assertTrue with null + 1 + + Assertion may be simplified using assertNull + + + + + + UseAssertNullInsteadOfAssertTrue: assertFalse with != null + 1 + + Assertion may be simplified using assertNull + + + + + + UseAssertNullInsteadOfAssertTrue: assertTrue with x == y + 1 + + Assertion may be simplified using assertSame + + + + + + UseAssertNullInsteadOfAssertTrue: Not a JUnit test - assertTrue with null + 0 + + + + + UseAssertNullInsteadOfAssertTrue: JUnit 4 - assertTrue with null + 1 + + + + + UseAssertNullInsteadOfAssertTrue: JUnit 5 - assertTrue with null - @Test + 1 + + + + + UseAssertNullInsteadOfAssertTrue: JUnit 5 - Assertions.assertTrue + 1 + + + + + + + UseAssertSameInsteadOfAssertTrue: assert true a == b + 1 + + + + + UseAssertSameInsteadOfAssertTrue: assert true a != b + 1 + + + + + UseAssertSameInsteadOfAssertTrue: assert false a == b + 1 + + + + + UseAssertSameInsteadOfAssertTrue: assert false a != b + 1 + + + + + UseAssertSameInsteadOfAssertTrue: bug 1626715, the null check in the rule shouldn't match the null outside the assert method + 1 + + Assertion may be simplified using assertNotSame + + + + + + UseAssertSameInsteadOfAssertTrue: assert true a == b BUT not a Junit test + 0 + + + + + UseAssertSameInsteadOfAssertTrue: JUnit 4 - assert true a == b + 1 + + + + Do not FP if we don't know where the method is coming from + 0 + + + + + UseAssertSameInsteadOfAssertTrue: JUnit 5 - assert true a == b - @Test + 1 + + + + UseAssertSameInsteadOfAssertTrue: JUnit 5 - assert true a == b - with qualifier + 1 + + + + + + + assertFalse(!) + 1 + + + + + assertTrue(!) + 1 + + + + + ok + 0 + + + + + not a JUnit test - assertFalse(!) + 0 + + + + + JUnit 4 - assertFalse(!) + 1 + + + + + JUnit 5 - assertFalse(!) + 1 + + + + + Use assert equals: positive test 1 + 1 + + Assertion may be simplified using assertEquals + + + + + + Use assert equals: positive test 2 + 1 + + Assertion may be simplified using assertNotEquals + + + + + + Use assert equals: positive test 3 + 1 + + Assertion may be simplified using assertEquals + + + + + Use assert equals: positive test 4 + 1 + + Assertion may be simplified using assertNotEquals + + + + + + Use assert equals: negative test + 0 + + + + + Use assert equals: Not a JUnit test + 0 + + + + + Use assert equals: JUnit4 + 1 + + + + + Use assert equals: JUnit5 - @Test + 1 + + Assertion may be simplified using assertEquals + + + + + + + JUnit Test contains assertEquals on other than boolean literal + 0 + + + + + JUnit Test contains assertEquals on boolean literal + 5 + + + + JUnit Test contains assertEquals on boolean with other thing not bool + 0 + + + + + #1323 False positive case of UseAssertTrueInsteadOfAssertEquals + 0 + + + + + JUnit Test contains assertEquals with Boxed booleans + 0 + + + + + + JUnit Test contains assertEquals with Boxed booleans as param + 0 + + + + + AssertEquals with call chain and not operator + 2 + + Assertion may be simplified using assertNotEquals + Assertion may be simplified using assertEquals + + + + + assertSame is not applicable on primitives + 2 + + Assertion may be simplified using assertNotSame + Assertion may be simplified using assertNotEquals + + + + + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedFormalParameter.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedFormalParameter.xml index 38fce0976c..b52248275a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedFormalParameter.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedFormalParameter.xml @@ -76,6 +76,7 @@ class Foo { flag public methods if checkall property is set true 1 + 2 @@ -307,6 +308,32 @@ class Imp1 { int ignoredArg2, int unused, String ignored) {} +} + ]]> + + + + anonymous classes defined in methods should be considered + false + 2 + 4,12 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml index 9d1af9043a..c7a7cc3169 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml @@ -420,4 +420,45 @@ public class UnusedLocalVariable { } ]]> + + + False positive with try-with-resources #3656 + 0 + + + + + False positive with variable used in unary expression #3671 + 0 + " + i++; + System.out.println(id); + } + } + public void run2(String ...args) { + int x; + for (String a : args) { + String id = a + " -> " + (++x); + System.out.println(id); + } + } +} + ]]> + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml index 0c240e4049..daf6776c9d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml @@ -654,6 +654,21 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class Foo { private String bar; +} + ]]> + + + + False positive with field used in unary expression #3671 + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertEqualsInsteadOfAssertTrue.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertEqualsInsteadOfAssertTrue.xml deleted file mode 100644 index b604960de4..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertEqualsInsteadOfAssertTrue.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - Identity test doesn't match - 0 - - - - - Junit 3 problem - 1 - - - - - Overload of equal doesn't match - 0 - - - - - JUnit 4, even outside of @Test method - 1 - - - - - JUnit4 - match - 1 - - - - - JUnit5 - @Test - 1 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertNullInsteadOfAssertTrue.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertNullInsteadOfAssertTrue.xml deleted file mode 100644 index fdd79d4e26..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertNullInsteadOfAssertTrue.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - assertTrue with null - 1 - - - - - assertFalse with != null - 1 - - - - - assertTrue with x == y - 0 - - - - - Not a JUnit test - assertTrue with null - 0 - - - - - JUnit 4 - assertTrue with null - 1 - - - - - JUnit 5 - assertTrue with null - @Test - 1 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertSameInsteadOfAssertTrue.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertSameInsteadOfAssertTrue.xml deleted file mode 100644 index 5a0a74559e..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertSameInsteadOfAssertTrue.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - assert true a == b - 1 - - - - - assert true a != b - 1 - - - - - assert false a == b - 1 - - - - - assert false a != b - 1 - - - - - skip assertTrue(x == null), UseAssertNullInsteadOfAssertTrue will pick those up - 0 - - - - - bug 1626715, the null check in the rule shouldn't match the null outside the assert method - 1 - - - - - assert true a == b BUT not a Junit test - 0 - - - - - JUnit 4 - assert true a == b - 1 - - - - - JUnit 5 - assert true a == b - @Test - 1 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertTrueInsteadOfAssertEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertTrueInsteadOfAssertEquals.xml deleted file mode 100644 index 13358b5119..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertTrueInsteadOfAssertEquals.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - JUnit Test contains assertEquals on other than boolean literal - 0 - - - - - JUnit Test contains assertEquals on boolean literal - 5 - - - - - #1323 False positive case of UseAssertTrueInsteadOfAssertEquals - 0 - - - - - JUnit Test contains assertEquals with Boxed booleans - 8 - - - - - JUnit Test contains assertEquals with Boxed booleans as param - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml index 9cb1804c1c..9bb180e1a3 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml @@ -463,7 +463,6 @@ public record CollectionRecord(List theList) { } } ]]> - java 15-preview @@ -479,6 +478,41 @@ public class Foo { if (theList.size() == 0) throw new IllegalArgumentException("empty list"); if (theList.isEmpty()) throw new IllegalArgumentException("empty list"); } +} + ]]> + + + + With nested classes + 3 + 7,15,21 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseTryWithResources.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseTryWithResources.xml index 61bf50e07f..2f3dab0ddd 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseTryWithResources.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseTryWithResources.xml @@ -219,6 +219,7 @@ public class TryWithResources { } ]]> + [java] UseTryWithResources - false negative for explicit close #2882 1 @@ -276,4 +277,118 @@ public class TryWithResources { } ]]> + + + + + [java] UseTryWithResources false positive when closeable is provided as a method argument or class field #3235 before java 9 + 0 + + java 1.8 + + + + [java] UseTryWithResources false positive when closeable is provided as a method argument or class field #3235 + 2 + 7,19 + + + + + + + [java] UseTryWithResources with local var and before java 9 #3235 + 2 + 8,20 + + java 1.8 + + + + [java] UseTryWithResources with local var and latest java #3235 + 2 + 8,20 + + + + + NPE when determining closeTarget + 1 + 6 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ClassNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ClassNamingConventions.xml index 388370286b..2067dee5a4 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ClassNamingConventions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ClassNamingConventions.xml @@ -46,13 +46,7 @@ public class foo { ]]> - - Utility class convention - 1 - - The utility class name 'Foo' doesn't match '[A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants)' - - + ]]> + + + Utility class convention (default) - #3563 + 0 + + + + + Utility class convention + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) + 1 + 1 + + The utility class name 'Foo' doesn't match '[A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants)' + + Class with only empty decls should not be a utility class + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 Class with some instance fields should not be a utility class + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 Class with static initializer alone should not be a utility class + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 Class with instance initializer should not be a utility class + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 Class with only static members except constructors should be a utility class + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 1 The utility class name 'Foo' doesn't match '[A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants)' @@ -167,6 +182,7 @@ public class Foo { Class with only constructors should not be a utility class + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 Class extending another class should not be utility class + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 { @@ -201,6 +218,7 @@ public class StringList extends java.util.ArrayList { Class extending another class should not be utility class 2 + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 Class with only main method should not be utility class + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 Class with only main method should not be utility class - varargs case + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 Class with main method and private static fields should not be utility class + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 Utility class can have name constants + [A-Z][a-zA-Z0-9]+(Utils?|Helper|Constants) 0 - - - - ok - 0 - - - - - bad - 1 - - - - - interface methods are always public - 0 - - - - - interface field are always public - 0 - - - - - bad - 1 - - - - - #1410 DefaultPackage triggers on field annotated with @VisibleForTesting - 0 - - - - - #2573 DefaultPackage triggers on field annotated with JUnit 5 @Test - 0 - - - - - #2573 DefaultPackage triggers on field annotated with JUnit 5 @RepeatedTest - 0 - - - - - #2573 DefaultPackage triggers on field annotated with JUnit 5 @ParameterizedTest - 0 - - - - - #2573 DefaultPackage triggers on field annotated with JUnit 5 @TestFactory - 0 - - - - - #2573 DefaultPackage triggers on field annotated with JUnit 5 @TestTemplate - 0 - - - - - #2573 DefaultPackage triggers on field annotated with JUnit 5 @BeforeAll - 0 - - - - - #2573 DefaultPackage triggers on field annotated with JUnit 5 @AfterAll - 0 - - - - - #2573 DefaultPackage triggers on field annotated with JUnit 5 @BeforeEach - 0 - - - - - #2573 DefaultPackage triggers on field annotated with JUnit 5 @AfterEach - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml index bfd40f5d92..f1cdcecd88 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml @@ -178,14 +178,16 @@ public class TournamentTest { JUnit 5 test detection [a-z][A-Za-z]*Test - 2 - 5,9 + 3 + 6,10,14 The JUnit 5 test method name 'testGetBestTeam' doesn't match '[a-z][A-Za-z]*Test' The JUnit 5 test method name 'getBestTeam' doesn't match '[a-z][A-Za-z]*Test' + The JUnit 5 test method name 'getWorstTeam' doesn't match '[a-z][A-Za-z]*Test' + + + + pos - new Integer(int) + 1 + 2 + + + + + neg - new Integer(String) + 0 + + + + + neg - Integer.valueOf(String) + 0 + + + + + pos - Integer.valueOf(int) + 1 + 2 + + Unnecessary explicit boxing + + + + + pos - new Integer(int) + 1 + 2 + + Unnecessary explicit boxing + + + + + pos - new Integer(int) with widening + 1 + 3 + + Unnecessary explicit conversion from char to Integer + + + + + pos - new Integer(int) assigned to object + 1 + 3 + + Unnecessary explicit boxing + + + + + pos - unboxing + 3 + 3,4,5 + + Unnecessary explicit unboxing + Unnecessary explicit conversion from Integer to double + Unnecessary explicit conversion from Integer to double + + + + + neg - unboxing to smaller type + 0 + + + + pos - unboxing to smaller type + 5 + 5,6,7,8,9 + + Unnecessary explicit boxing + Unnecessary explicit unboxing + Unnecessary explicit unboxing + Unnecessary explicit boxing + Unnecessary boxing of boxed value + + + + + + Unnecessary (primitive -> primitive) casts + 1 + 5 + + Unnecessary explicit boxing + + + + + Patch 2075906: Add toString() to the rule UnnecessaryWrapperObjectCreation + 1 + + Unnecessary explicit boxing + + + + + + #1057 False positive for UnnecessaryWrapperObjectCreation + 1 + 3 + + Unnecessary explicit conversion from int to Float + + + + + + Uses of Integer.valueOf(someString) where an int is expected + 4 + 3,4,5,6 + + Unnecessary implicit unboxing. Use Integer.parseInt(...) instead + Unnecessary implicit unboxing. Use Integer.parseInt(...) instead + Unnecessary implicit unboxing. Use Integer.parseInt(...) instead + Unnecessary implicit unboxing. Use Integer.parseInt(...) instead + + + + + + Uses of Long.valueOf(someString) where an long is expected + 4 + 3,4,5,6 + + Unnecessary implicit unboxing. Use Long.parseLong(...) instead + Unnecessary implicit unboxing. Use Long.parseLong(...) instead + Unnecessary implicit unboxing. Use Long.parseLong(...) instead + Unnecessary implicit unboxing. Use Long.parseLong(...) instead + + + + + + Uses of Double.valueOf(someString) where an double is expected + 2 + 3,4 + + Unnecessary implicit unboxing. Use Double.parseDouble(...) instead + Unnecessary implicit unboxing. Use Double.parseDouble(...) instead + + + + + + Uses of Float.valueOf(someString) where an float is expected + 2 + 3,4 + + Unnecessary implicit unboxing. Use Float.parseFloat(...) instead + Unnecessary implicit unboxing. Use Float.parseFloat(...) instead + + + + + + Uses of Short.valueOf(someString) where an short is expected + 4 + 3,4,5,6 + + Unnecessary implicit unboxing. Use Short.parseShort(...) instead + Unnecessary implicit unboxing. Use Short.parseShort(...) instead + Unnecessary implicit unboxing. Use Short.parseShort(...) instead + Unnecessary implicit unboxing. Use Short.parseShort(...) instead + + + + + + Uses of Boolean.valueOf(someString) where an boolean is expected + 2 + 3,4 + + Unnecessary implicit unboxing. Use Boolean.parseBoolean(...) instead + Unnecessary implicit unboxing. Use Boolean.parseBoolean(...) instead + + + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryReturn.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryReturn.xml index 28b3274464..09a62b6d1b 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryReturn.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryReturn.xml @@ -172,6 +172,7 @@ public class Foo { } ]]> + If within initializer - no violation 0 @@ -191,6 +192,7 @@ public class Foo { } ]]> + Return in fallthrough switch 1 @@ -212,6 +214,7 @@ public class Foo { } ]]> + Return in fallthrough switch (with blocks) 1 @@ -238,6 +241,7 @@ public class Foo { } ]]> + Return in switch arrow: violation 2 @@ -263,6 +267,7 @@ public class Foo { } ]]> + Return in switch arrow: no violation 0 @@ -289,6 +294,7 @@ public class Foo { } ]]> + Return in switch expression: no violation 0 @@ -313,6 +319,7 @@ public class Foo { } ]]> + Return in lambda x ctor 2 @@ -336,6 +343,7 @@ public class Foo { } ]]> + Return in loop (like break) 0 @@ -356,6 +364,7 @@ public class Foo { } ]]> + Ignore local class statements 1 @@ -370,4 +379,40 @@ public class Foo { } ]]> + + + nested classes + 4 + 6,12,19,26 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ClassWithOnlyPrivateConstructorsShouldBeFinal.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ClassWithOnlyPrivateConstructorsShouldBeFinal.xml index 538aca8368..1dd2843109 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ClassWithOnlyPrivateConstructorsShouldBeFinal.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ClassWithOnlyPrivateConstructorsShouldBeFinal.xml @@ -132,6 +132,68 @@ class ClassWithOnlyPrivateConstructorsShouldBeFinal { public String getString() { return string; } +} + ]]> + + + #2536 [java] ClassWithOnlyPrivateConstructorsShouldBeFinal can't detect inner class with only private constructor + 1 + 2 + + + + Private inner class with no ctor + 1 + 2 + + + + + Private abstract classes with abstract methods should be ignored #3668 + 0 + + + + + Private abstract classes without abstract methods but with subclasses should be ignored #3668 + 0 + + + + + Private abstract classes without abstract methods and subclasses #3668 + 1 + 2 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/CouplingBetweenObjects.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/CouplingBetweenObjects.xml index ffaf712658..c99e2d1cb3 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/CouplingBetweenObjects.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/CouplingBetweenObjects.xml @@ -8,6 +8,7 @@ lots of coupling 2 1 + 1 0 + + + + lots of coupling in inner class in interfaces + 2 + 1 + 1 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ExceptionAsFlowControl.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ExceptionAsFlowControl.xml index e42e514696..5804c4b079 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ExceptionAsFlowControl.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/ExceptionAsFlowControl.xml @@ -7,6 +7,7 @@ failure case 1 + 9 simple failure case 1 + 2 non-primitive failure case - only works for String 1 + 2 #2708 - Should trigger with non-lombok @Builder.Default fields 1 + 11 @@ -143,6 +146,7 @@ public class ExampleClass { Should trigger multiple times for fields declared on one line 2 + 5,5 failure case, inequality 1 + 3 failure case, comparison 1 + 3 + + + + negated negation + 1 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifiedTernary.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifiedTernary.xml index 2002135780..e3305ee2ff 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifiedTernary.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifiedTernary.xml @@ -9,7 +9,7 @@ 1 1 1 1 condition ? true : false - 0 + 1 @@ -69,8 +69,8 @@ public class Foo { 0 @@ -81,7 +81,7 @@ public class SimplifiedTernary { 0 - - - - assertFalse(!) - 1 - - - - - assertTrue(!) - 1 - - - - - ok - 0 - - - - - not a JUnit test - assertFalse(!) - 0 - - - - - JUnit 4 - assertFalse(!) - 1 - - - - - JUnit 5 - assertFalse(!) - 1 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifyConditional.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifyConditional.xml index 32846c87f2..ad2f14df16 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifyConditional.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SimplifyConditional.xml @@ -98,11 +98,10 @@ public class Bug1843273 { Bug 2317099 : False + in SimplifyConditional 0 failure case 1 + 3 @@ -308,6 +309,7 @@ public class Foo { 1409944, fields not used to synchronize should trigger 1 + 3 multiple fields on same line 1 + 2 [java] SingularField: Lombok false positive with annotated inner class 1 + 9 + + + failure case with anonymous class + 4 + 4,13,20,23 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SwitchDensity.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SwitchDensity.xml index 8d3c5fc1e2..8af61b0af7 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SwitchDensity.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/SwitchDensity.xml @@ -8,6 +8,7 @@ Five stmts in one switch case, should be flagged 4 1 + 4 + + Switch expr + 4 + 1 + + + + Switch expr, composite label + 4 + 0 + + One stmt in one switch case, ok @@ -69,6 +114,44 @@ public class SwitchDensity3 { } } } +} + ]]> + + + + False positive with default label + 10 + 0 + + + + Empty switch + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/UseUtilityClass.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/UseUtilityClass.xml index 81ab0468f8..32af58ff8f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/UseUtilityClass.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/UseUtilityClass.xml @@ -7,6 +7,9 @@ should be utility class since all static, public constructor 1 + + This utility class has a non-private constructor + - junit 'suite' method is OK + junit 3 'suite' method is OK 0 + + junit 4 'suite' method is OK + 0 + + Reproducing bug [ 2315599 ] False +: UseSingleton with class containing constructor: Although there is a static method, the class also has a non-private constructor. This is a common design for custom exceptions which contain a private static method to format error message strings. @@ -206,19 +226,17 @@ public class MyException extends RuntimeException { #1467 UseUtilityClass can't correctly check functions with multiple annotations 0 @@ -257,21 +275,6 @@ public class Foo { ]]> - - Lombok NoArgsConstructor no import- ok - 0 - - - Lombok NoArgsConstructor with no access level- should be a utility class 1 @@ -352,9 +355,21 @@ public class MyUtil { Inner class in abstract class false-negative 1 + 2 + + + Private inner class in abstract class + 0 + 1 1 Inner class in sub-class false-negative 1 + 2 call super 1 + 6 call super with same argument 1 + 6 call super with different argument 0 call super with different argument 2 0 @@ -57,7 +76,11 @@ public class Foo extends Bar { call super with different argument 3 0 call super with inverted arguments 0 return value of super 1 + 6 return value of super with argument 1 + 6 return value of super after adding a string 0 do not crash on abstract methods 0 do not crash on interfaces 0 0 @@ -161,9 +209,13 @@ public class Foo { call super with different argument 4 0 @@ -173,6 +225,10 @@ public class Buz{ adding final is OK 0 adding synchronized is OK, see sf-bug #423 0 Constructors are OK 0 Should ignore clone implementation ( see bug 1522517) 0 clone method with arguments should not be ignored 1 - False +: Overriding method merely calls super (see bug 1415525) + False +: Overriding method merely calls super - see bug 1415525 0 [ 1977230 ] false positive: UselessOverridingMethod 0 @@ -282,7 +355,9 @@ class Bar { [ 2142986 ] UselessOverridingMethod doesn't consider annotations, ignoreAnnotations property set to true true 1 + 10 [ 2142986 ] UselessOverridingMethod doesn't consider annotations 0 [ 2142986 ] UselessOverridingMethod doesn't consider annotations, @Override only 1 + 7 @@ -398,10 +482,22 @@ import net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod.BaseCla public class DirectSubclassInOtherPackage extends BaseClass { + // overrides to make the method available in this package + @Override protected void doBase() { super.doBase(); } + + @Override + protected void doBaseWithArg(String foo) { + super.doBaseWithArg(foo); + } + + @Override + protected void doBaseWithArgs(String foo, int bar) { + super.doBaseWithArgs(foo, bar); + } } ]]> @@ -414,26 +510,49 @@ package net.sourceforge.pmd.lang.java.rule.design.uselessoverridingmethod; public class TransitiveSubclass extends OtherSubclass { + // overrides to make the methods public + @Override public void doBase() { super.doBase(); } + + @Override + public void doBaseWithArg(String foo) { + super.doBaseWithArg(foo); + } + + @Override + public void doBaseWithArgs(String foo, int bar) { + super.doBaseWithArgs(foo, bar); + } } ]]> [java] UselessOverridingMethod false positive when elevating access modifier #911 - direct2 - 1 - 5 + 3 + 6,11,16 @@ -472,6 +591,28 @@ public class UselessOverridingMethodHashCode { public boolean equals(Object obj) { return super.equals(obj); } +} + ]]> + + + + Not overriding but overloading + 1 + 11 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AssignmentToNonFinalStatic.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AssignmentToNonFinalStatic.xml index f3c9bacb45..580dfa6d8e 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AssignmentToNonFinalStatic.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AssignmentToNonFinalStatic.xml @@ -8,6 +8,9 @@ clear rule violation 1 4 + + Possible unsafe assignment to non-final static field 'x' in a constructor. + + + clear rule violation with this + 1 + 4 + + Possible unsafe assignment to non-final static field 'x' in a constructor. + + + + ok 0 @@ -35,6 +55,10 @@ public class Foo { rule violated twice 2 4,5 + + Possible unsafe assignment to non-final static field 'x' in a constructor. + Possible unsafe assignment to non-final static field 'x' in a constructor. + + + + + legitimate case - assignment in a regular method instead of constructor + 0 + + + + + violation with increments + 2 + 6,7 + + Possible unsafe assignment to non-final static field 'counter' in a constructor. + Possible unsafe assignment to non-final static field 'counter' in a constructor. + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidAccessibilityAlteration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidAccessibilityAlteration.xml new file mode 100644 index 0000000000..b20329d34b --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidAccessibilityAlteration.xml @@ -0,0 +1,187 @@ + + + + + Example code snippet + 2 + 11,15 + constructor = this.getClass().getDeclaredConstructor(String.class); + // call to forbidden setAccessible + constructor.setAccessible(true); + + Method privateMethod = this.getClass().getDeclaredMethod("aPrivateMethod"); + // call to forbidden setAccessible + privateMethod.setAccessible(true); + + // deliberate accessibility alteration + String privateField = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public String run() { + try { + Field field = Violation.class.getDeclaredField("aPrivateField"); + field.setAccessible(true); + return (String) field.get(null); + } catch (ReflectiveOperationException | SecurityException e) { + throw new RuntimeException(e); + } + } + }); + } +} + ]]> + + + + Detect calls to setAccessible + 9 + 9,12,13,16,19,20,23,26,27 + constructor : this.getClass().getConstructors()) { + constructor.setAccessible(true); + } + Constructor[] constructors = this.getClass().getConstructors(); + AccessibleObject.setAccessible(constructors, true); + Constructor.setAccessible(constructors, true); + + for (Method method : this.getClass().getMethods()) { + method.setAccessible(true); + } + Method[] methods = this.getClass().getMethods(); + AccessibleObject.setAccessible(methods, true); + Method.setAccessible(methods, true); + + for (Field field : this.getClass().getFields()) { + field.setAccessible(true); + } + Field[] fields = this.getClass().getFields(); + AccessibleObject.setAccessible(fields, true); + Field.setAccessible(fields, true); + } +} + ]]> + + + + Make sure to detect method call chains + 6 + 3,4,5,6,7,8 + + + + + Anonymous privileged action is OK + 0 + () { + @Override + public Method[] run() { + Method[] declaredMethods = NoViolation.class.getDeclaredMethods(); + AccessibleObject.setAccessible(declaredMethods, true); + return declaredMethods; + } + }); + try { + methods[0].invoke(null); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } +} + ]]> + + + + Inner class privileged action is OK + 0 + { + @Override + public Field[] run() { + Field[] declaredFields = Violation.class.getDeclaredFields(); + AccessibleObject.setAccessible(declaredFields, true); + return declaredFields; + } + } +} + ]]> + + + + false positive when accessible object is used as primary prefix + 0 + > list = new ArrayList<>(); + Constructor ctor = NoViolation.class.getConstructor(); + list.add(ctor); +} } + ]]> + + + + setAccessible(false) is ok + 0 + constructor = this.getClass().getDeclaredConstructor(String.class); + // call to setAccessible with false - that's ok + constructor.setAccessible(false); + + Constructor[] constructors = this.getClass().getConstructors(); + AccessibleObject.setAccessible(constructors, false); + Constructor.setAccessible(constructors, false); + } +} + ]]> + + \ No newline at end of file diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidBranchingStatementAsLastInLoop.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidBranchingStatementAsLastInLoop.xml index 69f5b69865..cb83bafd0a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidBranchingStatementAsLastInLoop.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidBranchingStatementAsLastInLoop.xml @@ -13,6 +13,9 @@ public class Foo { return true; } } + for (int i = 0; i < 10;) + if (b) + return true; for (int i = 0; i < 10;) { if (b) { break; @@ -23,11 +26,22 @@ public class Foo { continue; } } + for (String s : new String[] { "a", "b" }) { + if (b) { + return; + } + } + for (String s : new String[] { "a", "b" }) + if (b) + return; while (true) { if (b) { return true; } } + while (true) + if (b) + return true; while (true) { if (b) { break; @@ -43,6 +57,14 @@ public class Foo { return true; } } while (true); + do { + if (b) + return true; + } while (true); + do + if (b) + return true; + while (true); do { if (b) { break; @@ -70,6 +92,15 @@ public class Foo { for (int i = 0; i < 10;) { continue; } + for (String s : new String[] { "a", "b" }) { + return; + } + for (String s : new String[] { "a", "b" }) { + break; + } + for (String s : new String[] { "a", "b" }) { + continue; + } while (true) { return true; } @@ -100,7 +131,8 @@ public class Foo { violations: break:for/do/while, continue:for/do/while and return:for/do/while - 9 + 12 + 5,8,11,14,17,20,23,26,29,32,35,38 @@ -109,7 +141,8 @@ public class Foo { for|do|while - 3 + 4 + 8,17,26,35 @@ -118,7 +151,8 @@ public class Foo { for|do|while - 3 + 4 + 11,20,29,38 @@ -127,7 +161,8 @@ public class Foo { for|do|while - 3 + 4 + 5,14,23,32 @@ -136,7 +171,8 @@ public class Foo { for - 1 + 2 + 8,17 @@ -146,6 +182,7 @@ public class Foo { 1 + 35 @@ -155,6 +192,7 @@ public class Foo { 1 + 26 @@ -163,7 +201,8 @@ public class Foo { for - 1 + 2 + 11,20 @@ -173,6 +212,7 @@ public class Foo { do 1 + 38 @@ -182,6 +222,7 @@ public class Foo { while 1 + 29 @@ -190,7 +231,8 @@ public class Foo { for - 1 + 2 + 5,14 @@ -200,6 +242,7 @@ public class Foo { do 1 + 32 @@ -209,6 +252,7 @@ public class Foo { while 1 + 23 @@ -217,7 +261,7 @@ public class Foo { 0 + + + + Without blocks + 12 + 4,6,8,10,12,14,16,18,20,21,22,23 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidInstanceofChecksInCatchClause.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidInstanceofChecksInCatchClause.xml index 7457d06d16..b3ee85db09 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidInstanceofChecksInCatchClause.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidInstanceofChecksInCatchClause.xml @@ -7,6 +7,7 @@ bad, instanceof FooException 1 + 6 failure case 1 + 9 failure case but obfuscated 1 + 9 0 0 0 bad, toArray() with cast 1 + 8 ok, no cast 0 Ensuring we don't have a few specific false positives, see bug 1697397 0 #998 False positive ClassCastExceptionWithToArray with generics 1 + 6 { public static E[] toArrayWrapped(Collection c) { // although you won't get a classcastexception here on the following line E[] retVal = (E[]) c.toArray(); diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustBePublic.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustBePublic.xml index 777680e571..5189133f76 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustBePublic.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustBePublic.xml @@ -7,6 +7,7 @@ protected method clone 1 + 3 protected method clone 2 1 + 3 final class with protected clone method 1 + 3 protected method clone with Object as return type 1 + 3 public method clone with Object as return type 1 + 3 final class with public method clone with Object as return type 1 + 3 - - - - ok, throws CloneNotSupportedException - 0 - - - - - bad - 1 - - - - - final class, rule does not apply - 0 - - - - - testing with multiple methods - 1 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml index a037edd9a5..f7619c1e94 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml @@ -10,9 +10,10 @@ 0 connection not closed, should have failed java.sql.Connection,java.sql.Statement,java.sql.ResultSet 1 - 4 + 6 Ensure that resources like this Connection object are closed after use 1 1 @@ -132,8 +144,11 @@ public class BadClose { 0 commit,close DAOTransaction,java.sql.Connection,java.sql.Statement,java.sql.ResultSet 1 - 8 + 10 [1964798] 3 bugs in CloseResourceRule : Case failing with complete name - MyHelper.MyClose + MyHelper.myClose java.sql.Connection,java.sql.Statement,java.sql.ResultSet 0 1 @@ -222,8 +245,10 @@ public class Foo { 1 [1964798] 3 bugs in CloseResourceRule : If connection is returned, we should not log a violation. - Connection,Statement,ResultSet + java.sql.Connection,java.sql.Statement,java.sql.ResultSet 0 + + If connection is returned indirectly, we should not log a violation. + java.sql.Connection,java.sql.Statement,java.sql.ResultSet + 0 + + + invoke an external method that close the resource: bug 2920057 closeStatement,closeStatement,closeResultSet,closeConnexion @@ -287,10 +336,11 @@ public class StructureFactory { 2 @@ -317,9 +369,10 @@ public class StructureFactory { 1 0 0 0 #947 CloseResource rule fails if field is marked with annotation true 2 - 8,9 + 10,11 Ensure that resources like this Connection object are closed after use Ensure that resources like this Statement object are closed after use MyClass 0 1 0 0 1 #1375 CloseResource not detected properly - ok true 1 - 6 + 7 Ensure that resources like this ResultSet object are closed after use @@ -579,11 +649,12 @@ public class Foo { #1375 CloseResource not detected properly - false negative 1 - 6 + 7 Ensure that resources like this ResultSet object are closed after use @@ -609,12 +682,13 @@ public class Foo { java.sql.Connection,java.sql.Statement,java.sql.ResultSet,java.sql.PreparedStatement closeLocalResources,closeResultSet 1 - 13 + 14 @@ -681,11 +761,12 @@ public class CloseResource { false 2 - - connection not closed, should have failed - missing import - 1 - - - closed with try-with-resources 0 @@ -1008,20 +1072,22 @@ public class Foo { Correctly determine the type with method calls 2 - 6,7 + 8,9 Ensure that resources like this Connection object are closed after use Ensure that resources like this PreparedStatement object are closed after use JMS Connections without auxclasspath 1 - 10 + 11 Ensure that resources like this Connection object are closed after use @@ -1043,11 +1109,12 @@ import javax.jms.Connection; import javax.jms.Session; public class CloseResourceJMS { + private Factory resourceFactory = new Factory(); public void run() { - Session session = resourceFactory.getSession(resourceHolder); + Session session = resourceFactory.getSession(); if (session != null) { if (startConnection) { - Connection con = resourceFactory.getConnection(resourceHolder); + Connection con = resourceFactory.getConnection(); if (con != null) { con.start(); } @@ -1056,6 +1123,11 @@ public class CloseResourceJMS { } return null; } + + public class Factory { + Session getSession() { return null; } + Connection getConnection() { return null; } + } } ]]> @@ -1135,8 +1207,7 @@ public class CloseResourcePrintWriter { 7,8,10 Ensure that resources like this FileInputStream object are closed after use - - Ensure that resources like this InputStream object are closed after use + Ensure that resources like this Scanner object are closed after use Ensure that resources like this FileInputStream object are closed after use 0 @@ -1217,7 +1291,6 @@ public class CloseResourceFP { { + Stream> selectBest(Stream> raw); + } + public class MatchResult implements Comparable> { } + public class TextFlow { } } ]]> @@ -1279,12 +1358,13 @@ public class CloseResourceStatementFP { NullPointerException if type of method parameter is not known - 1 + 0 @@ -1358,7 +1438,7 @@ public class Foo { 1 5 - it is recommended to wrap resource in try-with-resource declaration directly + it is recommended to wrap resource 'ois' in try-with-resource declaration directly #2764 false-negative when re-assigning connection java.sql.Connection,java.sql.Statement,java.sql.ResultSet 1 - 7 + 8 'c' is reassigned, but the original instance is not closed 0 stream = Stream.of(2); + Stream stream = Stream.of(2); if (condition) { stream = stream.skip(2); } @@ -1680,4 +1762,243 @@ public class FalsePositive { } ]]> + + + NPE with null literal + 0 + + + + + False positive with multiple resources in try-with-resources + 0 + + + + + False positive with single resource in try-with-resources multiple lines + 0 + + + + + false positive with reassignment after null check + 0 + + + + + false negative with classloader + 1 + 6 + + Ensure that resources like this URLClassLoader object are closed after use + + + + + + a directory stream build from a path as formal parameter should be reported + 1 + 8 + + Ensure that resources like this DirectoryStream<Path> object are closed after use + + ds = Files.newDirectoryStream(dir); + } +} + ]]> + + + + a returned stream should not be reported + 0 + list(Path dir) throws IOException { + DirectoryStream ds = Files.newDirectoryStream(dir); + return ds; + } +} + + ]]> + + + + a returned, wrapped stream should not be reported + 0 + list(Path dir) throws IOException { + DirectoryStream ds = Files.newDirectoryStream(dir); + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(ds.iterator, Spliterator.DISTINCT), false); + } +} + ]]> + + + + a reassignment that wraps the original stream should not be reported + 0 + + + + + stream in for-each-loop + 1 + 6 + + + + + ClassCastException with local record + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/DoNotCallGarbageCollectionExplicitly.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/DoNotCallGarbageCollectionExplicitly.xml index 7c8c22ec73..3f1fde0bb6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/DoNotCallGarbageCollectionExplicitly.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/DoNotCallGarbageCollectionExplicitly.xml @@ -7,6 +7,7 @@ Basic test case 1 + 4 Alernate Basic test case 2 + 4,9 Call in a constructor 1 + 4 + + + + run finalization + 2 + 3,4 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/DontImportSun.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/DontImportSun.xml index 5c9218db9f..b78a893d15 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/DontImportSun.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/DontImportSun.xml @@ -14,8 +14,8 @@ public class Bar {} - ok, signal is ok - 0 + signal is not ok + 1 basic test case 1 + 5 - assignment of a variable (local or field) to itself + assignment of a variable (field) to itself 1 + + + assignment of a variable (local) to itself + 1 + + + + no fp for unknown reference + 0 + 0 #1104 IdempotentOperation false positive 0 + + #1104 IdempotentOperation false positive (compilable) + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingBreakInSwitch.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/ImplicitSwitchFallThrough.xml similarity index 100% rename from pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingBreakInSwitch.xml rename to pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/ImplicitSwitchFallThrough.xml diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/InvalidLogMessageFormat.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/InvalidLogMessageFormat.xml index fba60aab81..f50d28dcd9 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/InvalidLogMessageFormat.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/InvalidLogMessageFormat.xml @@ -134,23 +134,26 @@ public class Test { @@ -175,6 +178,27 @@ public class InvalidSl4jExceptionBug1541 { } ]]> + + #3560 [java] InvalidLogMessageFormat: False positive with message and exception in a block inside a lambda + 0 + build() { + return e -> { + if (e instanceof RuntimeException) { + LOGGER.warn("Unexpected RuntimeException", e); + } + }; + } +} + ]]> + #1551 [java] InvalidSlf4jMessageFormat: fails with NPE @@ -226,6 +250,7 @@ public class Foo 0 0 missing argument in static block 1 - 8 + 9 0 missing argument in lambda call 1 - 9 + 11 @@ -599,6 +632,7 @@ public class Foo 0 0 log4j2: missing argument in static block 1 - 8 + 9 0 log4j2: missing argument in lambda call 1 - 9 + 11 ignore slf4j-Markers when detecting the number of arguments #2250 - 2 - 11,17 + 1 + 12 @@ -821,6 +862,7 @@ public class InvalidLogMessageFormatTest { - + #2431 IndexOutOfBoundsException when only logging exception message 0 @@ -879,7 +921,7 @@ public class Foo { private static final Logger LOG = LoggerFactory.getLogger(Foo.class); public void bar() { try { - new File("/text.txt"); + new java.io.File("/text.txt"); } catch (Exception e) { LOG.warn(e.getMessage()); } @@ -893,8 +935,8 @@ public class Foo { 2 10,14 - Missing arguments, expected 2 arguments but have 1 - Too many arguments, expected 1 argument but have 2 + Missing arguments, expected 2 arguments but found 1 + Too many arguments, expected 1 argument but found 2 - 1 + nested loop, wrong incrementer 1 + 4 - 2 + correct incrementer in nested loop 0 - 3 + loops with and without incrementer - all ok 0 - using outer loop incrementor as array index is OK + using outer loop incrementer as array index is OK 0 - + + + + No ForInit in nested loop - False negative + 1 + 6 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MisplacedNullCheck.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MisplacedNullCheck.xml index 08fd751764..7eafcdb5e6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MisplacedNullCheck.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MisplacedNullCheck.xml @@ -16,7 +16,7 @@ @@ -61,9 +63,11 @@ public class Foo { 0 @@ -77,7 +81,7 @@ public class Foo { 3372128: False positive: ArrayIsStoredDirectly 0 excludeStatus; public final void setExcludeStatus(String[] excludeStatus) { if (excludeStatus != null) { this.excludeStatus = Arrays.asList(excludeStatus.clone()); @@ -155,12 +162,13 @@ public class Test { False-positive/negative with multiple conditions 2 - 7,8 + 8,9 The null check here is misplaced; if the variable 'attributes' is null there will be a NullPointerException The null check here is misplaced; if the variable 'attributes' is null there will be a NullPointerException attributes) { boolean isStereotype = annotationType.equals("javax.inject.Named"); @@ -170,6 +178,27 @@ public class Test { if (isStereotype && attributes.containsKey("value") && attributes != null) {} if (isStereotype || attributes.containsKey("value") || attributes == null) {} } +} + ]]> + + + + False positive with no dereferencing variable access + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingSerialVersionUID.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingSerialVersionUID.xml index e88e57ea14..3da6fa9c53 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingSerialVersionUID.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingSerialVersionUID.xml @@ -17,6 +17,8 @@ public class Foo { Simple failure case 1 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingStaticMethodInNonInstantiatableClass.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingStaticMethodInNonInstantiatableClass.xml index e67f9d3a29..8ff5df1cc2 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingStaticMethodInNonInstantiatableClass.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingStaticMethodInNonInstantiatableClass.xml @@ -26,6 +26,7 @@ public class Foo { simple failure 1 + 1 failure with multiple constructors 1 + 1 not ok, non-public static field 1 + 1 #1125 Missing Static Method In Non Instantiatable Class / Factory 0 #1832 but fail with both private constructors annotated with @PersistenceConstructor 1 + 3 #2102 [java] False positive MissingStaticMethodInNonInstantiatableClass when inheritors are instantiable 0 label inside switch 1 + 6 hash code only 1 + 2 equals only 1 + 2 equals sig uses String, not Object 1 + 5 overloaded hashCode, should fail on equals 1 + 2 implements interface other than Comparable, not resolvable 1 + 2 implements interface other than Comparable, resolvable (#1303 OverrideBothEqualsAndHashcodeRule does not work on class implements resolvable interfaces) 1 + 2 bad, Foo.clone() calls new Foo(); 1 + 2 bad, Foo.clone() calls new Foo(); 1 + 3 Ok 0 Wrong class name 1 + 4 @@ -28,6 +34,7 @@ public class Foo { Ok, special case 0 Enum wrong class name 1 + 5 @@ -52,6 +63,7 @@ public enum Foo { bug 1626232, a separate variable initialization should not confuse the rule 0 Enums shoud be accepted for declaration 0 bug 1626232, extra loggers with different names are not allowed by default (use NOPMD if you want them) 1 + 5 Custom logger-class with invalid logger variable name MyLog 1 + 4 Public logger 1 + 4 package-private logger 1 + 4 package-private logger non static 1 + 4 package-private logger non static and non final 1 + 4 Public logger when static final required 1 + 4 non-private Logger initialized as String constant from class name java.util.logging.Logger 1 + 4 Package-protected logger when static final is not required 1 + 4 Package-protected logger when static final is required 1 + 4 Check type resolution of logger with invalid logger specified org.slf4j.Logger 1 + 5 similar to special case but with creating a new logger instead of passing in 1 + 4 similar to special case but with creating a new logger instead of passing in 1 + 3 false negative with apache commons logging 1 + 5 - - - - Basic test case - 1 - - - - - good behavior should not trigger violation - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/ReturnEmptyCollectionRatherThanNull.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/ReturnEmptyCollectionRatherThanNull.xml new file mode 100644 index 0000000000..cd469cf2a1 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/ReturnEmptyCollectionRatherThanNull.xml @@ -0,0 +1,102 @@ + + + + Returning null array + 1 + 3 + + + + Nonnull empty array + 0 + + + + Returning null instead of collection (List) + 1 + 5 + bar() + { + // ... + return null; + } +} + ]]> + + + Returning proper empty collection + 0 + bar() + { + // ... + return Collections.emptyList(); + } +} + ]]> + + + Returning null instead of collection (Set) + 1 + 5 + bar() + { + // ... + return null; + } +} + ]]> + + + Returning null instead of collection (Map) + 1 + 5 + bar() + { + // ... + return null; + } +} + ]]> + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/SimpleDateFormatNeedsLocale.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/SimpleDateFormatNeedsLocale.xml index e535332ce5..1ff9dfc85a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/SimpleDateFormatNeedsLocale.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/SimpleDateFormatNeedsLocale.xml @@ -8,6 +8,8 @@ ok, uses a two arg constructor 0 bad, using the single-arg constructor 1 + 3 Not OK! Has overridden getInstance() methods 1 + 1 failure case 1 + 3 failure case 1 + 3 Good practice 0 @@ -17,8 +20,12 @@ public class SomeEJB extends EJBObject implements EJBLocalHome { Bad example 1 + 5 failure case 2 + 2,6 - should be flagged 1 + 3 + + Suspicious decimal characters following octal escape in string literal: \12 + 8 + should be flagged - different octal 1 + + Suspicious decimal characters following octal escape in string literal: \000 + 8 + should be flagged - different octal 1 + + Suspicious decimal characters following octal escape in string literal: \40 + 0 + [ 2050064 ] False + SuspiciousOctalEscape with backslash literal, second test case 1 + + Suspicious decimal characters following octal escape in string literal: \12 + 8 + inner class should get checked 1 + 2 + + + false positive with anonymous class inside test class + 0 + + + + + [java] TestClassWithoutTestCases reports wrong classes in a file #3624 + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnconditionalIfStatement.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnconditionalIfStatement.xml index b8a5c1edb6..8adff01f55 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnconditionalIfStatement.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnconditionalIfStatement.xml @@ -7,6 +7,7 @@ if (true) 1 + 3 if (false) 1 + 3 failure case 1 + 4 variations 4 + 4,5,6,7 - asserting true a ! + asserting true a ! (see also #3087) 1 + 14 - asserting false a ! + asserting false a ! (see also #3087) 1 + 14 @@ -78,8 +102,8 @@ public class Foo extends TestCase { @@ -100,6 +124,7 @@ public class Foo { JUnit 4 - failure case 1 + 5 JUnit 5 - failure case - @Test 1 + 6 @@ -22,7 +22,7 @@ public class Foo { @@ -34,7 +34,7 @@ public class Foo { @@ -46,7 +46,7 @@ public class Foo { @@ -57,13 +57,17 @@ public class Foo { 1 clazz) { return null; } + public String getVariableName() { return null; } + public String getImage() { return null; } } ]]> @@ -86,8 +90,11 @@ public class Foo { NullPointerException if in constructor test 0 + + + + Preferred approach - static toString + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnusedNullCheckInEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnusedNullCheckInEquals.xml index 2e2a2d355d..78562a0008 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnusedNullCheckInEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UnusedNullCheckInEquals.xml @@ -7,10 +7,12 @@ failure case 1 + 4 @@ -21,7 +23,8 @@ public class Foo { 0 0 0 0 @@ -70,10 +75,11 @@ public class Foo { 0 @@ -81,9 +87,10 @@ public class Foo { shouldn't this fail? Yes, it should. Fixed it, so that method calls to equals on variables are considered, too. 1 + 3 Arrays can't be compared directly but with Arrays.equals(). 0 + + + + False negative with another condition in the middle + 3 + 3,7,14 + otherClass) { + return thatString != null && otherClass == thatString.getClass() && other.equals(thatString); + } + + private boolean matches_if(String thatString, Object other, Class otherClass) { + if (thatString != null && otherClass == thatString.getClass() && other.equals(thatString)) { + return true; + } + return false; + } + + private boolean matches_var(String thatString, Object other, Class otherClass) { + boolean result = thatString != null && otherClass == thatString.getClass() && other.equals(thatString); + return result; + } + + private boolean matchesCorrected(String thatString, Object other, Class otherClass) { + return thatString != null && otherClass == thatString.getClass() && thatString.equals(other); + } +} + ]]> + + + + False positive with string literals / constants + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseCorrectExceptionLogging.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseCorrectExceptionLogging.xml index 7ea61c739b..d59ac2b5d5 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseCorrectExceptionLogging.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseCorrectExceptionLogging.xml @@ -8,12 +8,15 @@ ok 0 failure case - two calls 2 + 9,10 must be in a catch block 0 bug 1626232, the rule should not be confused by inner classes - 0 + 2 + 13,14 bug 1626232, should work with a static block 1 + 12 XPath problem: A sequence of more than one item is not allowed as the first argument of concat() 0 + + + + [java] UseCorrectExceptionLogging FP in 6.31.0 #3100 + 0 + + + + + False negative with string concatenation + 1 + 8 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml index 934090bf33..d535631744 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml @@ -144,6 +144,8 @@ public class O { #2979 UseEqualsToCompareStrings: FP with "var" variables 0 #2979 UseEqualsToCompareStrings: FP with "var" variables (control, types are explicit) 0 + + + False positive with string concatentation + 0 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseLocaleWithCaseConversions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseLocaleWithCaseConversions.xml index 65e3c3ea99..aad2a55166 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseLocaleWithCaseConversions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseLocaleWithCaseConversions.xml @@ -7,9 +7,12 @@ toLowerCase() with no args 1 + 3 @@ -17,9 +20,12 @@ public class Foo { toUpperCase() with no args 1 + 3 @@ -28,9 +34,13 @@ public class Foo { both ok 0 @@ -40,7 +50,9 @@ public class Foo { 0 @@ -49,6 +61,8 @@ public class Foo { Compound method call 0 #1556 [java] UseLocaleWithCaseConversions does not works with `ResultSet` (false negative) 1 + 7 1 @@ -37,6 +37,22 @@ public class UseProperClassLoaderFN { Class c = o.getClass(); ClassLoader cl2 = c.getClassLoader(); } +} + ]]> + + + + False positives (#3173) + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/NonThreadSafeSingleton.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/NonThreadSafeSingleton.xml index b085ea8ff6..23a897b482 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/NonThreadSafeSingleton.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/NonThreadSafeSingleton.xml @@ -7,11 +7,15 @@ failure case 1 + 7 buz; + public static List bar() { + if (buz == null) buz = new ArrayList<>(); return buz; } } @@ -22,10 +26,13 @@ public class Foo { OK, method is synchronized 0 buz; + public static synchronized List bar() { + if (buz == null) buz = new ArrayList<>(); return buz; } } @@ -36,11 +43,14 @@ public class Foo { OK, in synchronized block 0 buz; + public static List bar() { synchronized (baz) { - if (buz == null) buz = new ArrayList(); + if (buz == null) buz = new ArrayList<>(); return buz; } } @@ -52,10 +62,12 @@ public class Foo { OK, in returning non-static data 0 failure case, two if statements 1 + 8 buz; private static boolean b = false; - public static List bar(String foo) { + public static List bar(String foo) { if (buz == null) { - buz = new ArrayList(); + buz = new ArrayList<>(); if (foo == null) { b = true; } @@ -85,12 +101,16 @@ public class Foo { failure case, compound if statement 1 + 7 list; + public static List bar(String param) { if (list == null || !param.equals("foo")) { - list = new ArrayList(); + list = new ArrayList<>(); param = "x"; } return list; @@ -102,12 +122,16 @@ public class Foo { failure case 2 1 + 7 buz = null; + private static List bar() { if (buz == null) { - buz = Collections.get(Integer.MAX_SIZE); + buz = Collections. emptyList(); } return buz; } @@ -134,17 +158,39 @@ public class A { #997 Rule NonThreadSafeSingleton gives analysis problem 1 + 7 + + + + False positive with correct double checked pattern + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UnsynchronizedStaticFormatter.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UnsynchronizedStaticFormatter.xml index 1fa411b04e..c9012e95eb 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UnsynchronizedStaticFormatter.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UnsynchronizedStaticFormatter.xml @@ -7,6 +7,7 @@ Format called from non-synchronized block 1 + 6 3, Inside synchronized, Not OK 1 + 6 5, Use DateFormat, not ok 1 + 6 6, Use DateFormat, fail 1 + 6 #940 False positive on UnsynchronizedStaticDateFormatter 0 @@ -132,6 +142,7 @@ public class Test { NumberFormatters should be detected 1 + 6 MessageFormatters should be detected 1 + 6 0 #1815 static synchronized, Not OK 1 + 6 #1815 static synchronized, also Not OK 1 + 6 #1815 static synchronized, Not OK if allowMethodLevelSynchronization true 1 + 6 #1815 static synchronized, Not OK 1 + 7 + + + + Ignore in annotations + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidArrayLoops.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidArrayLoops.xml index 19e2627b52..b321d410d7 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidArrayLoops.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidArrayLoops.xml @@ -10,6 +10,7 @@ copy one array to another 1 + 4 copy via while loop 1 + 5 0 0 using an offset, still bad 1 + 4 0 0 + + + + Example code + 1 + 7 + + + + + False positive with calculated array index + 0 + + + + + False positive with same array + 0 + + + + + Correct example - System.arraycopy can be used + 1 + 4 + + + + + False positive with break/continue in loop + 0 + + + + + False positive with linked list + 0 + + + + + False positive with increment by 2 + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidCalendarDateCreation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidCalendarDateCreation.xml index ce34feba1f..374b93b004 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidCalendarDateCreation.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidCalendarDateCreation.xml @@ -135,6 +135,7 @@ public class Foo { + + + + False positive with Calendar not created here + 0 + + + + + False positive when this is a Calendar + 0 + + + + + False positive with calendar builder + 0 + + + + + False positive with specific date + 0 + + + + + False positive with calendar not created by getInstance + 0 + + + + + New GregorianCalendar default constructor + 2 + 7,8 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidInstantiatingObjectsInLoops.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidInstantiatingObjectsInLoops.xml index 247f7a8a33..6b975dc823 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidInstantiatingObjectsInLoops.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidInstantiatingObjectsInLoops.xml @@ -7,6 +7,7 @@ TEST1 1 + 4 TEST2 1 + 4 TEST3 1 + 4 TEST4 2 + 4,5 False positive when assigning to a list/array (see #2207 and #1043) 0 @@ -170,16 +177,23 @@ public class PMDDemo { False negative with break in other for-loop 1 - 7 + 14 getFilteredMessages( - String fileName, FileContents fileContents, DetailAST rootAST) { - final SortedSet result = new TreeSet<>(messages); - for (LocalizedMessage element : messages) { - final TreeWalkerAuditEvent event = - new TreeWalkerAuditEvent(fileContents, fileName, element, rootAST); - for (TreeWalkerFilter filter : filters) { +import java.util.Function; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +public class Message { + private List> filters; + + public Message(Message message) {} + + private SortedSet getFilteredMessages() { + final SortedSet result = new TreeSet<>(messages); + for (Message element : messages) { + Message event = new Message(element); + for (Function filter : filters) { if (!filter.accept(event)) { result.remove(element); break; @@ -256,6 +270,7 @@ public class Sample { Collections with unresolved type 1 + 9 + + + + False negative with array allocations within loops + 1 + 4 + + + + + False positive with for-each loop over new array + 0 + + + + + False positive with adding to a collection/array field + 0 + field = new ArrayList<>(); + private String[] arrayField = new String[10]; + + public static void main(String[] args) { + for (String arg : args) { + this.field.add(new String(arg)); + } + for (int i = 0; i < args.length; i++) { + this.arrayField[i] = new String(args[i]); + } + } +} + ]]> + + + + False positive with adding wrapped new arrays to collection + 0 + buffers = new ArrayList<>(); + for (byte b : bytes) { + buffers.add(ByteBuffer.wrap(new byte[]{b})); + } + } +} + ]]> + + + + False positive with temporary object assigned to an array + 0 + + + + + False positive with array allocation and assignment + 0 + + + + + False positive when array is returned + 0 + + + + + False positive when converting collection to array + 0 + data = new ArrayList<>(); + + for (String arg : args) { + this.consume(data.toArray(new String[0])); + } + } + + private void consume(String[] array) { } } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidUsingShortType.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidUsingShortType.xml deleted file mode 100644 index 95eecb27da..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidUsingShortType.xml +++ /dev/null @@ -1,144 +0,0 @@ - - - - - Short as field - 1 - - - - - Short as local variable - 1 - - - - - Short as method return type - 2 - - - - - Short as method return type - 0 - - - - - #1449 false positive when casting a variable to short - 0 - - - - - short as method parameter - 1 - - - - - short as method parameter with @Override - 0 - - - - - [java] AvoidUsingShortType erroneously triggered on overrides of 3rd party methods (anon. class) #586 - 0 - - - - - [java] AvoidUsingShortType erroneously triggered on overrides of 3rd party methods #586 - 0 - - - - - short as annotation property - 1 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/BigIntegerInstantiation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/BigIntegerInstantiation.xml index ef9c824349..2c62d72b52 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/BigIntegerInstantiation.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/BigIntegerInstantiation.xml @@ -7,6 +7,7 @@ Fail, BigInteger(1) 1 + 4 Fail, BigInteger(0) 1 + 4 Fail, BigInteger(10) 1.5 mode 1 + 4 Fail, BigDecimal(1) - 1 + 2 + 4,5 java 1.5 @@ -108,12 +113,14 @@ public class Foo { Fail, BigDecimal(10) - 1 + 2 + 4,5 java 1.5 @@ -121,14 +128,50 @@ public class Foo { Fail, BigDecimal(0) - 1 + 3 + 4,5,6 java 1.5 + + + Fail, BigInteger(2) with Java9 + 1 + 4 + + java 9 + + + + False negative with indirect const string + 1 + 5 + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/BooleanInstantiation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/BooleanInstantiation.xml deleted file mode 100644 index 4d38c36a64..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/BooleanInstantiation.xml +++ /dev/null @@ -1,163 +0,0 @@ - - - - - simple failure case - 1 - - - - - new java.lang.Boolean - 1 - - - - - ok - 0 - - - - - don't use Boolean.valueOf() with literal - 2 - - - - - valueOf() with variable is fine - 0 - - - - - don't use Boolean.valueOf() with string literal - 1 - - - - - don't use Boolean.valueOf() in method call - 1 - - - - - don't use new Boolean() in method call - 1 - - - - - ok - 0 - - - - - ok - 0 - - - - - don't use new Boolean() in static block - 1 - - - - - Bug 1744065, should be ok - 0 - - - - - Test for failure after rule with custom Boolean, should report failure if rule reset done correctly - 1 - - - - - #1533 [java] BooleanInstantiation: ClassCastException with Annotation - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/ByteInstantiation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/ByteInstantiation.xml deleted file mode 100644 index 2cbd7a2b44..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/ByteInstantiation.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - new Byte(), bad - 1 - - - - - Byte.valueOf(), ok - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientStringBuffering.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientStringBuffering.xml index 0e7147f5f4..faaebfad2b 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientStringBuffering.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientStringBuffering.xml @@ -460,6 +460,24 @@ public class Foo { sb.append('}'); return sb.toString(); } +} + ]]> + + + + [java] NPE in InefficientStringBuffering with Records #3420 + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InsufficientStringBufferDeclaration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InsufficientStringBufferDeclaration.xml index c9a6913e37..f29d071f3d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InsufficientStringBufferDeclaration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InsufficientStringBufferDeclaration.xml @@ -1309,6 +1309,29 @@ public class InsufficientStringBufferDeclaration { msg.append("A very long text"); msg.append("that is longer than 16 characters"); } +} + ]]> + + + + NPE when StringBuilder is used in lambda and if without else + 0 + altns = new LinkedHashSet<>(); + StringBuilder sb = new StringBuilder(); + altns.forEach(s -> sb.append(System.lineSeparator()).append("\tat " + "foo")); + msg = sb.toString(); + } + } } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/IntegerInstantiation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/IntegerInstantiation.xml deleted file mode 100644 index 208c229141..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/IntegerInstantiation.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - new Integer(), bad - 1 - - - - - Integer.valueOf(), ok - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/LongInstantiation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/LongInstantiation.xml deleted file mode 100644 index 154dc5c2ab..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/LongInstantiation.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - new Long(), bad - 1 - - - - - Long.valueOf(), ok - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/ShortInstantiation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/ShortInstantiation.xml deleted file mode 100644 index 22f52ea6a1..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/ShortInstantiation.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - new Short(), bad - 1 - - - - - Short.valueOf(), ok - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml deleted file mode 100644 index 3a67cfa8b9..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - failure case - 1 - - - - - startsWith multiple chars - 0 - - - - - startsWith defined on some other class, doesn't take a String - 0 - - - - - Document Jaxen exception parsing Unicode chars in startsWith - 0 - - - - - #1392 SimplifyStartsWith false-negative - 1 - 5 - - - - #2712 SimplifyStartsWith false-positive on receiver != String - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UnnecessaryWrapperObjectCreation.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UnnecessaryWrapperObjectCreation.xml deleted file mode 100644 index 9f235e0047..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UnnecessaryWrapperObjectCreation.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - failure case - 1 - - - - - calling valueOf is OK - 0 - - - - - failure case for 1.5+ - 1 - - - - - Patch 2075906: Add toString() to the rule UnnecessaryWrapperObjectCreation - 1 - - - - - #1057 False positive for UnnecessaryWrapperObjectCreation - 1 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml index 30c7e0968d..e7cd9a26e5 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml @@ -142,6 +142,7 @@ public class Test { } ]]> + FP with char array valueOf 0 @@ -152,6 +153,51 @@ public class Test { // if we remove the valueOf, then the toString of the array would be printed return "Literal(" + String.valueOf(this.text) + ")"; } +} + ]]> + + + + #3492 False positive for UselessStringValueOf, when there is no initial String to append to + 0 + + + + + #3491 False positive for UselessStringValueOf when valueOf(char [], int, int) is used + 0 + + + + + #3491 False positive for UselessStringValueOf when valueOf(char [], int, int) is used + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/security/xml/HardCodedCryptoKey.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/security/xml/HardCodedCryptoKey.xml index 9a7a976ca4..a7627a403f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/security/xml/HardCodedCryptoKey.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/security/xml/HardCodedCryptoKey.xml @@ -7,6 +7,7 @@ Hard coded inline cryptographic key, bad 1 + 6 Hard coded in field cryptographic key, bad 1 + 5 + + + + [java] HardcodedCryptoKey false negative with variable assignments #3368 + 3 + 7,9,16 + diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTFunctionCall.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTFunctionCall.java index 4b338e3bac..91d64330bb 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTFunctionCall.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTFunctionCall.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.ecmascript.ast; import org.mozilla.javascript.ast.FunctionCall; -public final class ASTFunctionCall extends AbstractEcmascriptNode { +public final class ASTFunctionCall extends AbstractFunctionCallNode { ASTFunctionCall(FunctionCall functionCall) { super(functionCall); } @@ -15,20 +15,4 @@ public final class ASTFunctionCall extends AbstractEcmascriptNode protected R acceptJsVisitor(EcmascriptVisitor visitor, P data) { return visitor.visit(this, data); } - - public EcmascriptNode getTarget() { - return (EcmascriptNode) getChild(0); - } - - public int getNumArguments() { - return node.getArguments().size(); - } - - public EcmascriptNode getArgument(int index) { - return (EcmascriptNode) getChild(index + 1); - } - - public boolean hasArguments() { - return getNumArguments() != 0; - } } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTNewExpression.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTNewExpression.java index cf23c62924..79b6d6d78c 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTNewExpression.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTNewExpression.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.ecmascript.ast; import org.mozilla.javascript.ast.NewExpression; -public final class ASTNewExpression extends AbstractEcmascriptNode { +public final class ASTNewExpression extends AbstractFunctionCallNode { ASTNewExpression(NewExpression newExpression) { super(newExpression); } @@ -16,22 +16,6 @@ public final class ASTNewExpression extends AbstractEcmascriptNode getTarget() { - return (EcmascriptNode) getChild(0); - } - - public int getNumArguments() { - return node.getArguments().size(); - } - - public EcmascriptNode getArgument(int index) { - return (EcmascriptNode) getChild(index + 1); - } - - public boolean hasArguments() { - return getNumArguments() != 0; - } - public boolean hasInitializer() { return node.getInitializer() != null; } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/AbstractFunctionCallNode.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/AbstractFunctionCallNode.java new file mode 100644 index 0000000000..a336a07726 --- /dev/null +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/AbstractFunctionCallNode.java @@ -0,0 +1,31 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ecmascript.ast; + +import org.mozilla.javascript.ast.FunctionCall; + +abstract class AbstractFunctionCallNode extends AbstractEcmascriptNode { + + AbstractFunctionCallNode(T node) { + super(node); + } + + public EcmascriptNode getTarget() { + return (EcmascriptNode) getChild(0); + } + + public int getNumArguments() { + return node.getArguments().size(); + } + + public EcmascriptNode getArgument(int index) { + return (EcmascriptNode) getChild(index + 1); + } + + public boolean hasArguments() { + return getNumArguments() != 0; + } + +} diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/rule/AbstractEcmascriptRule.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/rule/AbstractEcmascriptRule.java index 5fa7392d77..c3f3eec489 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/rule/AbstractEcmascriptRule.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/rule/AbstractEcmascriptRule.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -17,5 +17,4 @@ public abstract class AbstractEcmascriptRule extends AbstractRule public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); } - } diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/AbstractJspRule.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/AbstractJspRule.java index e24a7e9e9e..09aa1d202c 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/AbstractJspRule.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/AbstractJspRule.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt index 9ea953c086..2a09f7874f 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt @@ -46,8 +46,8 @@ abstract class CpdTextComparisonTest( */ @JvmOverloads fun doTest(fileBaseName: String, expectedSuffix: String = "", properties: Properties = defaultProperties()) { - super.doTest(fileBaseName, expectedSuffix) { sourceText -> - val sourceCode = SourceCode(SourceCode.StringCodeLoader(sourceText, "$fileBaseName$extensionIncludingDot")) + super.doTest(fileBaseName, expectedSuffix) { fileData -> + val sourceCode = SourceCode(SourceCode.StringCodeLoader(fileData.fileText, fileData.fileName)) val tokens = Tokens().also { val tokenizer = newTokenizer(properties) tokenizer.tokenize(sourceCode, it) @@ -58,10 +58,18 @@ abstract class CpdTextComparisonTest( } @JvmOverloads - fun expectTokenMgrError(source: String, properties: Properties = defaultProperties()): TokenMgrError = - shouldThrow { - newTokenizer(properties).tokenize(sourceCodeOf(source), Tokens()) - } + fun expectTokenMgrError( + source: String, + fileName: String = SourceCode.StringCodeLoader.DEFAULT_NAME, + properties: Properties = defaultProperties() + ): TokenMgrError = + expectTokenMgrError(FileData(fileName, source), properties) + + @JvmOverloads + fun expectTokenMgrError(fileData: FileData, properties: Properties = defaultProperties()): TokenMgrError = + shouldThrow { + newTokenizer(properties).tokenize(sourceCodeOf(fileData), Tokens()) + } private fun StringBuilder.format(tokens: Tokens) { @@ -140,11 +148,13 @@ abstract class CpdTextComparisonTest( fun sourceCodeOf(str: String): SourceCode = SourceCode(SourceCode.StringCodeLoader(str)) + fun sourceCodeOf(fileData: FileData): SourceCode = + SourceCode(SourceCode.StringCodeLoader(fileData.fileText, fileData.fileName)) fun tokenize(tokenizer: Tokenizer, str: String): Tokens = - Tokens().also { - tokenizer.tokenize(sourceCodeOf(str), it) - } + Tokens().also { + tokenizer.tokenize(sourceCodeOf(str), it) + } private companion object { const val Indent = " " diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index 22e43f6004..707836b8e2 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -61,7 +61,8 @@ abstract class BaseParsingHelper, T : RootNode fun getVersion(version: String?): LanguageVersion { val language = language return if (version == null) language.defaultVersion - else language.getVersion(version) ?: throw AssertionError("Unsupported version $version for language $language") + else language.getVersion(version) + ?: throw AssertionError("Unsupported version $version for language $language") } val language: Language @@ -117,11 +118,15 @@ abstract class BaseParsingHelper, T : RootNode * so. */ @JvmOverloads - fun parse(sourceCode: String, version: String? = null, filename: String = TextFile.UNKNOWN_FILENAME): T { + open fun parse( + sourceCode: String, + version: String? = null, + fileName: String = TextFile.UNKNOWN_FILENAME + ): T { val lversion = if (version == null) defaultVersion else getVersion(version) val handler = lversion.languageVersionHandler val parser = handler.parser - val textDoc = TextDocument.readOnlyString(sourceCode, filename, lversion) + val textDoc = TextDocument.readOnlyString(sourceCode, fileName, lversion) val task = Parser.ParserTask(textDoc, SemanticErrorReporter.noop()) task.properties.also { handler.declareParserTaskProperties(it) @@ -165,14 +170,14 @@ abstract class BaseParsingHelper, T : RootNode */ @JvmOverloads open fun parseResource(resource: String, version: String? = null): T = - parse(readResource(resource), version) + parse(readResource(resource), version, fileName = resource) /** * Fetches and [parse]s the [path]. */ @JvmOverloads open fun parseFile(path: Path, version: String? = null): T = - parse(IOUtils.toString(Files.newBufferedReader(path)), version, filename = path.toAbsolutePath().toString()) + parse(IOUtils.toString(Files.newBufferedReader(path)), version, fileName = path.toAbsolutePath().toString()) /** * Fetches the source of the given [clazz]. @@ -221,7 +226,11 @@ abstract class BaseParsingHelper, T : RootNode * found by the rule. The language version of the piece of code is determined by the [params]. */ @JvmOverloads - fun executeRule(rule: Rule, code: String, filename: String = "testfile.${language.extensions[0]}"): Report { + fun executeRule( + rule: Rule, + code: String, + fileName: String = "testfile.${language.extensions[0]}" + ): Report { val config = PMDConfiguration().apply { suppressMarker = params.suppressMarker setDefaultLanguageVersion(defaultVersion) @@ -233,7 +242,7 @@ abstract class BaseParsingHelper, T : RootNode AbstractPMDProcessor.runSingleFile( listOf(RuleSet.forSingleRule(rule)), - TextFile.forCharSeq(code, filename, defaultVersion), + TextFile.forCharSeq(code, fileName, defaultVersion), fullListener, config ) @@ -244,6 +253,4 @@ abstract class BaseParsingHelper, T : RootNode fun executeRuleOnResource(rule: Rule, resourcePath: String): Report = executeRule(rule, readResource(resourcePath)) - - } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseTreeDumpTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseTreeDumpTest.kt index 2201b384a8..49af1e58ac 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseTreeDumpTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseTreeDumpTest.kt @@ -32,9 +32,13 @@ abstract class BaseTreeDumpTest( */ @JvmOverloads fun doTest(fileBaseName: String, parser: BaseParsingHelper<*, *> = this.parser) { - super.doTest(fileBaseName, "") { sourceText -> + super.doTest(fileBaseName, "") { fileData -> buildString { - printer.renderSubtree(parser.parse(sourceText), this) + val ast = parser.parse( + sourceCode = fileData.fileText, + fileName = fileData.fileName + ) + printer.renderSubtree(ast, this) } } } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/test/BaseTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/test/BaseTextComparisonTest.kt index b7a0cc9816..cb45c4ed41 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/test/BaseTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/test/BaseTextComparisonTest.kt @@ -27,6 +27,8 @@ abstract class BaseTextComparisonTest { /** Extension that the unparsed source file is supposed to have. */ protected abstract val extensionIncludingDot: String + data class FileData(val fileName:String, val fileText:String) + /** * Executes the test. The test files are looked up using the [parser]. * The reference test file must be named [fileBaseName] + [ExpectedExt]. @@ -37,7 +39,7 @@ abstract class BaseTextComparisonTest { */ internal fun doTest(fileBaseName: String, expectedSuffix: String = "", - transformTextContent: (String) -> String) { + transformTextContent: (FileData) -> String) { val expectedFile = findTestFile(resourceLoader, "${resourcePrefix}/$fileBaseName$expectedSuffix$ExpectedExt").toFile() val actual = transformTextContent(sourceText(fileBaseName)) @@ -56,7 +58,7 @@ abstract class BaseTextComparisonTest { assertEquals(expected.normalize(), actual.normalize(), "File comparison failed, see the reference: $expectedFile") } - protected fun sourceText(fileBaseName: String): String { + protected fun sourceText(fileBaseName: String): FileData { val sourceFile = findTestFile(resourceLoader, "${resourcePrefix}/$fileBaseName$extensionIncludingDot").toFile() assert(sourceFile.isFile) { @@ -64,7 +66,7 @@ abstract class BaseTextComparisonTest { } val sourceText = sourceFile.readText(Charsets.UTF_8).normalize() - return sourceText + return FileData(fileName = sourceFile.toString(), fileText = sourceText) } protected open fun String.normalize() = replace( diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java index 15690e3b81..417cd767fb 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/rule/AbstractModelicaRule.java @@ -33,5 +33,4 @@ public abstract class AbstractModelicaRule extends AbstractRule implements Model } return true; } - } diff --git a/pmd-plsql/etc/grammar/PLSQL.jjt b/pmd-plsql/etc/grammar/PLSQL.jjt index 10e7ed7065..f01c74a1b7 100644 --- a/pmd-plsql/etc/grammar/PLSQL.jjt +++ b/pmd-plsql/etc/grammar/PLSQL.jjt @@ -27,6 +27,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** + * Add support for Select statement within OPEN FOR Statements + * + * Andreas Dangel 09/2021 + *==================================================================== * Add support for XMLROOT, improve ExtractExpression to support xml * * Piotr Szymanski 03/2020 @@ -84,7 +88,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Andreas Dangel 07/2018 *==================================================================== - * Added ASTIsOfTypeCondition node, added support for USING IN|OUT|IN_OUT + * Added ASTIsOfTypeCondition node, added support for USING IN|OUT|IN OUT * See PMD Bug #1520 * * Andreas Dangel 11/2016 @@ -1997,10 +2001,13 @@ ASTIntoClause IntoClause() : { return jjtThis; } } +// This might also be a associative array dereference... +// see https://github.com/pmd/pmd/issues/3515 ASTVariableName VariableName() : -{ ASTID id; StringBuilder name = new StringBuilder(); } +{ ASTID id; ASTLiteral lit; StringBuilder name = new StringBuilder(); } { id = ID() { name.append(id.getImage()); } + [ "(" lit = Literal() ")" { name.append('(').append(lit.getImage()).append(')'); } ] [ "." id = ID() { name.append('.').append(id.getImage()); } ] [ "." id = ID() { name.append('.').append(id.getImage()); } ] { @@ -2714,13 +2721,17 @@ ASTCloseStatement CloseStatement() : { jjtThis.setImage(cursor.getImage()) ; return jjtThis ; } } +/* + * See https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/OPEN-statement.html#GUID-FB5A9CC3-655F-4AF4-8105-14CB39F2FEA8 + * and https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/OPEN-FOR-statement.html#GUID-EB7AF439-FDD3-4461-9E3F-B621E8ABFB96 + */ ASTOpenStatement OpenStatement() : {} { - [Expression()] - //[LOOKAHEAD(functionCall()) functionCall() | QualifiedName()] - [ Expression() [ ( | | )? Expression() ("," ( | | )? Expression())*]] - { return jjtThis ; } + [Expression()] + [ ( SelectStatement() | Expression() ) [ UsingClause() ] ] + + { return jjtThis ; } } /* @@ -2741,12 +2752,18 @@ ASTEmbeddedSqlStatement EmbeddedSqlStatement() : { StringExpression() [ Name() ("," Name())* ] - [ [ [ ] | ] Expression() ("," [ [ ] | ] Expression())* ] + [ UsingClause() ] [ ( | ) Expression() ("," Expression())*] ";" // PIPELINED FUNCTION OUTPUT { return jjtThis ; } } +void UsingClause() #void : +{} +{ + [ [ ] | ] Expression() ("," [ [ ] | ] Expression())* +} + ASTPipelineStatement PipelineStatement() : {} { @@ -4873,7 +4890,6 @@ TOKEN [IGNORE_CASE]: | | | - | | | | diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java index b8483cbb5e..ddf6f17c3c 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/AbstractPLSQLRule.java @@ -85,7 +85,6 @@ public abstract class AbstractPLSQLRule extends AbstractRule implements PLSQLPar return false; } - @Override public boolean dependsOn(AstProcessingStage stage) { if (!(stage instanceof PlsqlProcessingStage)) { diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/CyclomaticComplexityRule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/CyclomaticComplexityRule.java index 90ae8aa44f..dc85b93b4a 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/CyclomaticComplexityRule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/CyclomaticComplexityRule.java @@ -269,6 +269,16 @@ public class CyclomaticComplexityRule extends AbstractPLSQLRule { return data; } + private void updateClassEntry(int methodDecisionPoints) { + Entry classEntry = entryStack.peek(); + classEntry.methodCount++; + classEntry.bumpDecisionPoints(methodDecisionPoints); + + if (methodDecisionPoints > classEntry.highestDecisionPoints) { + classEntry.highestDecisionPoints = methodDecisionPoints; + } + } + @Override public Object visit(ASTProgramUnit node, Object data) { LOGGER.entering(CLASS_NAME, "visit(ASTProgramUnit)"); @@ -297,24 +307,10 @@ public class CyclomaticComplexityRule extends AbstractPLSQLRule { * ASTTypeMethod * */ - Entry classEntry = entryStack.peek(); - classEntry.methodCount++; - classEntry.bumpDecisionPoints(methodDecisionPoints); - - if (methodDecisionPoints > classEntry.highestDecisionPoints) { - classEntry.highestDecisionPoints = methodDecisionPoints; - } - } - - ASTMethodDeclarator methodDeclarator = null; - for (int n = 0; n < node.getNumChildren(); n++) { - Node childNode = node.getChild(n); - if (childNode instanceof ASTMethodDeclarator) { - methodDeclarator = (ASTMethodDeclarator) childNode; - break; - } + updateClassEntry(methodDecisionPoints); } + ASTMethodDeclarator methodDeclarator = node.getFirstChildOfType(ASTMethodDeclarator.class); if (methodEntry.decisionPoints >= reportLevel) { addViolation(data, node, new String[] { "method", methodDeclarator == null ? "" : methodDeclarator.getImage(), @@ -344,24 +340,10 @@ public class CyclomaticComplexityRule extends AbstractPLSQLRule { * TODO This does not cope with nested methods We need the * outer most ASTPackageBody */ - Entry classEntry = entryStack.peek(); - classEntry.methodCount++; - classEntry.bumpDecisionPoints(methodDecisionPoints); - - if (methodDecisionPoints > classEntry.highestDecisionPoints) { - classEntry.highestDecisionPoints = methodDecisionPoints; - } - } - - ASTMethodDeclarator methodDeclarator = null; - for (int n = 0; n < node.getNumChildren(); n++) { - Node childNode = node.getChild(n); - if (childNode instanceof ASTMethodDeclarator) { - methodDeclarator = (ASTMethodDeclarator) childNode; - break; - } + updateClassEntry(methodDecisionPoints); } + ASTMethodDeclarator methodDeclarator = node.getFirstChildOfType(ASTMethodDeclarator.class); if (methodEntry.decisionPoints >= reportLevel) { addViolation(data, node, new String[] { "method", methodDeclarator == null ? "" : methodDeclarator.getImage(), @@ -392,15 +374,7 @@ public class CyclomaticComplexityRule extends AbstractPLSQLRule { classEntry.highestDecisionPoints = methodDecisionPoints; } - ASTMethodDeclarator methodDeclarator = null; - for (int n = 0; n < node.getNumChildren(); n++) { - Node childNode = node.getChild(n); - if (childNode instanceof ASTMethodDeclarator) { - methodDeclarator = (ASTMethodDeclarator) childNode; - break; - } - } - + ASTMethodDeclarator methodDeclarator = node.getFirstChildOfType(ASTMethodDeclarator.class); if (methodEntry.decisionPoints >= reportLevel) { addViolation(data, node, new String[] { "method", methodDeclarator == null ? "" : methodDeclarator.getImage(), diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/LocalScope.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/LocalScope.java index 27cdec5d0d..59f5abef4b 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/LocalScope.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/LocalScope.java @@ -4,21 +4,16 @@ package net.sourceforge.pmd.lang.plsql.symboltable; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.plsql.ast.ASTName; import net.sourceforge.pmd.lang.plsql.ast.InternalApiBridge; -import net.sourceforge.pmd.lang.symboltable.AbstractScope; -import net.sourceforge.pmd.lang.symboltable.Applier; -import net.sourceforge.pmd.lang.symboltable.ImageFinderFunction; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; -public class LocalScope extends AbstractScope { +public class LocalScope extends MethodOrLocalScope { @Override public Set addNameOccurrence(NameOccurrence occ) { @@ -37,31 +32,6 @@ public class LocalScope extends AbstractScope { return declarations; } - public Map> getVariableDeclarations() { - return getDeclarations(VariableNameDeclaration.class); - } - - @Override - public void addDeclaration(NameDeclaration declaration) { - if (declaration instanceof VariableNameDeclaration && getDeclarations().keySet().contains(declaration)) { - throw new RuntimeException(declaration + " is already in the symbol table"); - } - super.addDeclaration(declaration); - } - - public Set findVariableHere(PLSQLNameOccurrence occurrence) { - Set result = new HashSet<>(); - if (occurrence.isThisOrSuper() || occurrence.isMethodOrConstructorInvocation()) { - return result; - } - ImageFinderFunction finder = new ImageFinderFunction(occurrence.getImage()); - Applier.apply(finder, getVariableDeclarations().keySet().iterator()); - if (finder.getDecl() != null) { - result.add(finder.getDecl()); - } - return result; - } - @Override public String toString() { return "LocalScope:" + getVariableDeclarations().keySet(); diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/MethodOrLocalScope.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/MethodOrLocalScope.java new file mode 100644 index 0000000000..415b8b5d25 --- /dev/null +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/MethodOrLocalScope.java @@ -0,0 +1,43 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.plsql.symboltable; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.sourceforge.pmd.lang.symboltable.AbstractScope; +import net.sourceforge.pmd.lang.symboltable.Applier; +import net.sourceforge.pmd.lang.symboltable.ImageFinderFunction; +import net.sourceforge.pmd.lang.symboltable.NameDeclaration; +import net.sourceforge.pmd.lang.symboltable.NameOccurrence; + +abstract class MethodOrLocalScope extends AbstractScope { + @Override + public void addDeclaration(NameDeclaration declaration) { + if (declaration instanceof VariableNameDeclaration && getDeclarations().keySet().contains(declaration)) { + throw new RuntimeException(declaration + " is already in the symbol table"); + } + super.addDeclaration(declaration); + } + + public Map> getVariableDeclarations() { + return getDeclarations(VariableNameDeclaration.class); + } + + public Set findVariableHere(PLSQLNameOccurrence occurrence) { + Set result = new HashSet<>(); + if (occurrence.isThisOrSuper() || occurrence.isMethodOrConstructorInvocation()) { + return result; + } + ImageFinderFunction finder = new ImageFinderFunction(occurrence.getImage()); + Applier.apply(finder, getVariableDeclarations().keySet().iterator()); + if (finder.getDecl() != null) { + result.add(finder.getDecl()); + } + return result; + } +} diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/MethodScope.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/MethodScope.java index a6107c91a3..91f8069b51 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/MethodScope.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/MethodScope.java @@ -4,22 +4,16 @@ package net.sourceforge.pmd.lang.plsql.symboltable; -import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Set; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.plsql.ast.ASTName; import net.sourceforge.pmd.lang.plsql.ast.InternalApiBridge; import net.sourceforge.pmd.lang.plsql.ast.PLSQLNode; -import net.sourceforge.pmd.lang.symboltable.AbstractScope; -import net.sourceforge.pmd.lang.symboltable.Applier; -import net.sourceforge.pmd.lang.symboltable.ImageFinderFunction; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; -public class MethodScope extends AbstractScope { +public class MethodScope extends MethodOrLocalScope { private final PLSQLNode node; @@ -31,10 +25,6 @@ public class MethodScope extends AbstractScope { return this; } - public Map> getVariableDeclarations() { - return getDeclarations(VariableNameDeclaration.class); - } - @Override public Set addNameOccurrence(NameOccurrence occ) { PLSQLNameOccurrence occurrence = (PLSQLNameOccurrence) occ; @@ -51,27 +41,6 @@ public class MethodScope extends AbstractScope { return declarations; } - @Override - public void addDeclaration(NameDeclaration declaration) { - if (declaration instanceof VariableNameDeclaration && getDeclarations().keySet().contains(declaration)) { - throw new RuntimeException(declaration + " is already in the symbol table"); - } - super.addDeclaration(declaration); - } - - public Set findVariableHere(PLSQLNameOccurrence occurrence) { - Set result = new HashSet<>(); - if (occurrence.isThisOrSuper() || occurrence.isMethodOrConstructorInvocation()) { - return result; - } - ImageFinderFunction finder = new ImageFinderFunction(occurrence.getImage()); - Applier.apply(finder, getVariableDeclarations().keySet().iterator()); - if (finder.getDecl() != null) { - result.add(finder.getDecl()); - } - return result; - } - public String getName() { return node.getChild(1).getCanonicalImage(); } diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/ScopeAndDeclarationFinder.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/ScopeAndDeclarationFinder.java index 783c3ec69c..e22fe60469 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/ScopeAndDeclarationFinder.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/symboltable/ScopeAndDeclarationFinder.java @@ -261,10 +261,9 @@ public class ScopeAndDeclarationFinder extends PLSQLParserVisitorAdapter { * } } } cont(node); return data; } */ - @Override - public Object visit(ASTTypeMethod node, Object data) { + private Object visitMethodLike(PLSQLNode node, Object data) { createMethodScope(node); - ASTMethodDeclarator md = node.getFirstChildOfType(ASTMethodDeclarator.class); + final ASTMethodDeclarator md = node.getFirstChildOfType(ASTMethodDeclarator.class); // A PLSQL Method (FUNCTION|PROCEDURE) may be schema-level try { node.getScope().getEnclosingScope(ClassScope.class).addDeclaration(new MethodNameDeclaration(md)); @@ -276,7 +275,7 @@ public class ScopeAndDeclarationFinder extends PLSQLParserVisitorAdapter { } if ("getEnclosingClassScope() called on SourceFileScope".equals(e.getMessage())) { if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest("ClassScope skipped for Schema-level method: methodName=" + node.getMethodName() + LOGGER.finest("ClassScope skipped for Schema-level method: methodName=" + md.getImage() + "; Image=" + node.getImage()); } @@ -286,7 +285,7 @@ public class ScopeAndDeclarationFinder extends PLSQLParserVisitorAdapter { if (1 < on.getNumChildren()) { ASTID schemaName = on.getFirstChildOfType(ASTID.class); if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest("SchemaName for Schema-level method: methodName=" + node.getMethodName() + LOGGER.finest("SchemaName for Schema-level method: methodName=" + md.getImage() + "; Image=" + node.getImage() + "is " + schemaName.getImage()); } @@ -296,41 +295,15 @@ public class ScopeAndDeclarationFinder extends PLSQLParserVisitorAdapter { cont(node); return data; } + + @Override + public Object visit(ASTTypeMethod node, Object data) { + return visitMethodLike(node, data); + } @Override public Object visit(ASTProgramUnit node, Object data) { - createMethodScope(node); - ASTMethodDeclarator md = node.getFirstChildOfType(ASTMethodDeclarator.class); - // A PLSQL Method (FUNCTION|PROCEDURE) may be schema-level - try { - node.getScope().getEnclosingScope(ClassScope.class).addDeclaration(new MethodNameDeclaration(md)); - } catch (Exception e) { - // @TODO possibly add to a pseudo-ClassScope equivalent to the - // Schema name - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest("ProgramUnit getEnclosingClassScope Exception string=\"" + e.getMessage() + "\""); - } - if ("getEnclosingClassScope() called on SourceFileScope".equals(e.getMessage())) { - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest("ClassScope skipped for Schema-level method: methodName=" + node.getMethodName() - + "; Image=" + node.getImage()); - } - - // A File-level/Schema-level object may have a Schema-name - // explicitly specified in the declaration - ASTObjectNameDeclaration on = md.getFirstChildOfType(ASTObjectNameDeclaration.class); - if (1 < on.getNumChildren()) { - ASTID schemaName = on.getFirstChildOfType(ASTID.class); - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest("SchemaName for Schema-level method: methodName=" + node.getMethodName() - + "; Image=" + node.getImage() + "is " + schemaName.getImage()); - } - - } - } - } - cont(node); - return data; + return visitMethodLike(node, data); } // TODO - what about while loops and do loops? diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/PlsqlTreeDumpTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/PlsqlTreeDumpTest.java index 41367994a6..3a4c1cc8f8 100644 --- a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/PlsqlTreeDumpTest.java +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/PlsqlTreeDumpTest.java @@ -32,4 +32,13 @@ public class PlsqlTreeDumpTest extends BaseTreeDumpTest { doTest("ParsingExclusion"); } + @Test + public void parseOpenForStatement() { + doTest("OpenForStatement"); + } + + @Test + public void parseSelectIntoAssociativeArrayType() { + doTest("SelectIntoArray"); + } } diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/OpenForStatement.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/OpenForStatement.pls new file mode 100644 index 0000000000..331498fb09 --- /dev/null +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/OpenForStatement.pls @@ -0,0 +1,62 @@ +-- +-- See https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/OPEN-FOR-statement.html#GUID-EB7AF439-FDD3-4461-9E3F-B621E8ABFB96 +-- https://github.com/pmd/pmd/issues/3487 +-- + +CREATE OR REPLACE PROCEDURE EXAMPLE_PROCEDURE IS + -- + TYPE t_ref_cursor IS REF CURSOR; + -- + l_ref_cursor t_ref_cursor; + -- +BEGIN + -- + OPEN l_ref_cursor FOR + SELECT * + FROM DUAL; + -- +END EXAMPLE_PROCEDURE; + +-- +-- Example 6-26 Fetching Data with Cursor Variables +-- https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/static-sql.html#GUID-AA5A2016-1B76-4961-9AFB-EB052F0D0FB2 +-- +DECLARE + cv SYS_REFCURSOR; -- cursor variable + + v_lastname employees.last_name%TYPE; -- variable for last_name + v_jobid employees.job_id%TYPE; -- variable for job_id + + query_2 VARCHAR2(200) := + 'SELECT * FROM employees + WHERE REGEXP_LIKE (job_id, ''[ACADFIMKSA]_M[ANGR]'') + ORDER BY job_id'; + + v_employees employees%ROWTYPE; -- record variable row of table + +BEGIN + OPEN cv FOR + SELECT last_name, job_id FROM employees + WHERE REGEXP_LIKE (job_id, 'S[HT]_CLERK') + ORDER BY last_name; + + LOOP -- Fetches 2 columns into variables + FETCH cv INTO v_lastname, v_jobid; + EXIT WHEN cv%NOTFOUND; + DBMS_OUTPUT.PUT_LINE( RPAD(v_lastname, 25, ' ') || v_jobid ); + END LOOP; + + DBMS_OUTPUT.PUT_LINE( '-------------------------------------' ); + + OPEN cv FOR query_2; + + LOOP -- Fetches entire row into the v_employees record + FETCH cv INTO v_employees; + EXIT WHEN cv%NOTFOUND; + DBMS_OUTPUT.PUT_LINE( RPAD(v_employees.last_name, 25, ' ') || + v_employees.job_id ); + END LOOP; + + CLOSE cv; +END; +/ diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/OpenForStatement.txt b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/OpenForStatement.txt new file mode 100644 index 0000000000..e17fe0f407 --- /dev/null +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/OpenForStatement.txt @@ -0,0 +1,285 @@ ++- Input[@CanonicalImage = null, @ExcludedLinesCount = "0", @ExcludedRangesCount = "0", @Sourcecode = "--\n-- See https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/OPEN-FOR-statement.html#GUID-EB7AF439-FDD3-4461-9E3F-B621E8ABFB96\n-- https://github.com/pmd/pmd/issues/3487\n--\n\nCREATE OR REPLACE PROCEDURE EXAMPLE_PROCEDURE IS\n --\n TYPE t_ref_cursor IS REF CURSOR;\n --\n l_ref_cursor t_ref_cursor;\n --\nBEGIN\n --\n OPEN l_ref_cursor FOR\n SELECT *\n FROM DUAL;\n --\nEND EXAMPLE_PROCEDURE;\n\n--\n-- Example 6-26 Fetching Data with Cursor Variables\n-- https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/static-sql.html#GUID-AA5A2016-1B76-4961-9AFB-EB052F0D0FB2\n--\nDECLARE\n cv SYS_REFCURSOR; -- cursor variable\n \n v_lastname employees.last_name%TYPE; -- variable for last_name\n v_jobid employees.job_id%TYPE; -- variable for job_id\n \n query_2 VARCHAR2(200) :=\n \'SELECT * FROM employees\n WHERE REGEXP_LIKE (job_id, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY job_id\';\n \n v_employees employees%ROWTYPE; -- record variable row of table\n \nBEGIN\n OPEN cv FOR\n SELECT last_name, job_id FROM employees\n WHERE REGEXP_LIKE (job_id, \'S[HT]_CLERK\')\n ORDER BY last_name;\n \n LOOP -- Fetches 2 columns into variables\n FETCH cv INTO v_lastname, v_jobid;\n EXIT WHEN cv%NOTFOUND;\n DBMS_OUTPUT.PUT_LINE( RPAD(v_lastname, 25, \' \') || v_jobid );\n END LOOP;\n \n DBMS_OUTPUT.PUT_LINE( \'-------------------------------------\' );\n \n OPEN cv FOR query_2;\n \n LOOP -- Fetches entire row into the v_employees record\n FETCH cv INTO v_employees;\n EXIT WHEN cv%NOTFOUND;\n DBMS_OUTPUT.PUT_LINE( RPAD(v_employees.last_name, 25, \' \') ||\n v_employees.job_id );\n END LOOP;\n \n CLOSE cv;\nEND;\n/\n"] + +- Global[@CanonicalImage = null] + | +- ProgramUnit[@CanonicalImage = null, @MethodName = "EXAMPLE_PROCEDURE", @Name = "EXAMPLE_PROCEDURE", @ObjectName = null] + | +- MethodDeclarator[@CanonicalImage = "EXAMPLE_PROCEDURE", @Image = "EXAMPLE_PROCEDURE", @ParameterCount = "1"] + | | +- ObjectNameDeclaration[@CanonicalImage = "EXAMPLE_PROCEDURE", @Image = "EXAMPLE_PROCEDURE"] + | | +- ID[@CanonicalImage = "EXAMPLE_PROCEDURE", @Image = "EXAMPLE_PROCEDURE"] + | +- DeclarativeSection[@CanonicalImage = null] + | | +- DeclarativeUnit[@CanonicalImage = null] + | | | +- SubTypeDefinition[@CanonicalImage = "T_REF_CURSOR", @Image = "t_ref_cursor"] + | | | +- QualifiedID[@CanonicalImage = "T_REF_CURSOR", @Image = "t_ref_cursor"] + | | +- DeclarativeUnit[@CanonicalImage = null] + | | +- VariableOrConstantDeclaration[@CanonicalImage = null] + | | +- VariableOrConstantDeclarator[@CanonicalImage = "L_REF_CURSOR T_REF_CURSOR", @Image = "l_ref_cursor t_ref_cursor"] + | | +- VariableOrConstantDeclaratorId[@Array = "false", @ArrayDepth = "0", @CanonicalImage = "L_REF_CURSOR", @Image = "l_ref_cursor"] + | | | +- ID[@CanonicalImage = "L_REF_CURSOR", @Image = "l_ref_cursor"] + | | +- Datatype[@CanonicalImage = "T_REF_CURSOR", @Image = "t_ref_cursor", @TypeImage = "t_ref_cursor"] + | | +- QualifiedName[@CanonicalImage = "T_REF_CURSOR", @Image = "t_ref_cursor"] + | | +- UnqualifiedID[@CanonicalImage = "T_REF_CURSOR", @Image = "t_ref_cursor"] + | +- Statement[@CanonicalImage = null] + | | +- UnlabelledStatement[@CanonicalImage = null] + | | +- OpenStatement[@CanonicalImage = null] + | | +- Expression[@CanonicalImage = "L_REF_CURSOR", @Image = "l_ref_cursor"] + | | | +- PrimaryPrefix[@CanonicalImage = "L_REF_CURSOR", @Image = "l_ref_cursor", @SelfModifier = "false"] + | | | +- SimpleExpression[@CanonicalImage = "L_REF_CURSOR", @Image = "l_ref_cursor"] + | | | +- Column[@CanonicalImage = "L_REF_CURSOR", @Image = "l_ref_cursor"] + | | | +- ID[@CanonicalImage = "L_REF_CURSOR", @Image = "l_ref_cursor"] + | | +- SelectStatement[@All = "false", @CanonicalImage = null, @Distinct = "false", @Unique = "false"] + | | +- SelectList[@CanonicalImage = null] + | | +- FromClause[@CanonicalImage = null] + | | +- TableReference[@CanonicalImage = null] + | | +- TableName[@CanonicalImage = "DUAL", @Image = "DUAL"] + | | +- ID[@CanonicalImage = "DUAL", @Image = "DUAL"] + | +- ID[@CanonicalImage = "EXAMPLE_PROCEDURE", @Image = "EXAMPLE_PROCEDURE"] + +- Global[@CanonicalImage = null] + +- Block[@CanonicalImage = null] + +- DeclarativeSection[@CanonicalImage = null] + | +- DeclarativeUnit[@CanonicalImage = null] + | | +- VariableOrConstantDeclaration[@CanonicalImage = null] + | | +- VariableOrConstantDeclarator[@CanonicalImage = "CV SYS_REFCURSOR", @Image = "cv SYS_REFCURSOR"] + | | +- VariableOrConstantDeclaratorId[@Array = "false", @ArrayDepth = "0", @CanonicalImage = "CV", @Image = "cv"] + | | | +- ID[@CanonicalImage = "CV", @Image = "cv"] + | | +- Datatype[@CanonicalImage = "SYS_REFCURSOR", @Image = "SYS_REFCURSOR", @TypeImage = "SYS_REFCURSOR"] + | | +- ScalarDataTypeName[@CanonicalImage = "SYS_REFCURSOR", @Image = "SYS_REFCURSOR"] + | +- DeclarativeUnit[@CanonicalImage = null] + | | +- VariableOrConstantDeclaration[@CanonicalImage = null] + | | +- VariableOrConstantDeclarator[@CanonicalImage = "V_LASTNAME EMPLOYEES.LAST_NAME%TYPE", @Image = "v_lastname employees.last_name%TYPE"] + | | +- VariableOrConstantDeclaratorId[@Array = "false", @ArrayDepth = "0", @CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | | +- ID[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | +- Datatype[@CanonicalImage = "EMPLOYEES.LAST_NAME%TYPE", @Image = "employees.last_name%TYPE", @TypeImage = "employees.last_name%TYPE"] + | | +- QualifiedName[@CanonicalImage = "EMPLOYEES.LAST_NAME", @Image = "employees.last_name"] + | | +- UnqualifiedID[@CanonicalImage = "EMPLOYEES", @Image = "employees"] + | | +- QualifiedID[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | +- DeclarativeUnit[@CanonicalImage = null] + | | +- VariableOrConstantDeclaration[@CanonicalImage = null] + | | +- VariableOrConstantDeclarator[@CanonicalImage = "V_JOBID EMPLOYEES.JOB_ID%TYPE", @Image = "v_jobid employees.job_id%TYPE"] + | | +- VariableOrConstantDeclaratorId[@Array = "false", @ArrayDepth = "0", @CanonicalImage = "V_JOBID", @Image = "v_jobid"] + | | | +- ID[@CanonicalImage = "V_JOBID", @Image = "v_jobid"] + | | +- Datatype[@CanonicalImage = "EMPLOYEES.JOB_ID%TYPE", @Image = "employees.job_id%TYPE", @TypeImage = "employees.job_id%TYPE"] + | | +- QualifiedName[@CanonicalImage = "EMPLOYEES.JOB_ID", @Image = "employees.job_id"] + | | +- UnqualifiedID[@CanonicalImage = "EMPLOYEES", @Image = "employees"] + | | +- QualifiedID[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | +- DeclarativeUnit[@CanonicalImage = null] + | | +- VariableOrConstantDeclaration[@CanonicalImage = null] + | | +- VariableOrConstantDeclarator[@CanonicalImage = "QUERY_2 VARCHAR2(200) := \'SELECT * FROM EMPLOYEES\n WHERE REGEXP_LIKE (JOB_ID, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY JOB_ID\'", @Image = "query_2 VARCHAR2(200) := \'SELECT * FROM employees\n WHERE REGEXP_LIKE (job_id, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY job_id\'"] + | | +- VariableOrConstantDeclaratorId[@Array = "false", @ArrayDepth = "0", @CanonicalImage = "QUERY_2", @Image = "query_2"] + | | | +- ID[@CanonicalImage = "QUERY_2", @Image = "query_2"] + | | +- Datatype[@CanonicalImage = "VARCHAR2(200)", @Image = "VARCHAR2(200)", @TypeImage = "VARCHAR2(200)"] + | | | +- ScalarDataTypeName[@CanonicalImage = "VARCHAR2(200)", @Image = "VARCHAR2(200)"] + | | | +- NumericLiteral[@CanonicalImage = "200", @Image = "200"] + | | +- VariableOrConstantInitializer[@CanonicalImage = "\'SELECT * FROM EMPLOYEES\n WHERE REGEXP_LIKE (JOB_ID, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY JOB_ID\'", @Image = "\'SELECT * FROM employees\n WHERE REGEXP_LIKE (job_id, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY job_id\'"] + | | +- Expression[@CanonicalImage = "\'SELECT * FROM EMPLOYEES\n WHERE REGEXP_LIKE (JOB_ID, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY JOB_ID\'", @Image = "\'SELECT * FROM employees\n WHERE REGEXP_LIKE (job_id, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY job_id\'"] + | | +- PrimaryPrefix[@CanonicalImage = "\'SELECT * FROM EMPLOYEES\n WHERE REGEXP_LIKE (JOB_ID, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY JOB_ID\'", @Image = "\'SELECT * FROM employees\n WHERE REGEXP_LIKE (job_id, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY job_id\'", @SelfModifier = "false"] + | | +- Literal[@CanonicalImage = "\'SELECT * FROM EMPLOYEES\n WHERE REGEXP_LIKE (JOB_ID, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY JOB_ID\'", @Image = "\'SELECT * FROM employees\n WHERE REGEXP_LIKE (job_id, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY job_id\'"] + | | +- StringLiteral[@CanonicalImage = "\'SELECT * FROM EMPLOYEES\n WHERE REGEXP_LIKE (JOB_ID, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY JOB_ID\'", @Image = "\'SELECT * FROM employees\n WHERE REGEXP_LIKE (job_id, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY job_id\'", @String = "SELECT * FROM employees\n WHERE REGEXP_LIKE (job_id, \'\'[ACADFIMKSA]_M[ANGR]\'\')\n ORDER BY job_id"] + | +- DeclarativeUnit[@CanonicalImage = null] + | +- VariableOrConstantDeclaration[@CanonicalImage = null] + | +- VariableOrConstantDeclarator[@CanonicalImage = "V_EMPLOYEES EMPLOYEES%ROWTYPE", @Image = "v_employees employees%ROWTYPE"] + | +- VariableOrConstantDeclaratorId[@Array = "false", @ArrayDepth = "0", @CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | | +- ID[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | +- Datatype[@CanonicalImage = "EMPLOYEES%ROWTYPE", @Image = "employees%ROWTYPE", @TypeImage = "employees%ROWTYPE"] + | +- QualifiedName[@CanonicalImage = "EMPLOYEES", @Image = "employees"] + | +- UnqualifiedID[@CanonicalImage = "EMPLOYEES", @Image = "employees"] + +- Statement[@CanonicalImage = null] + | +- UnlabelledStatement[@CanonicalImage = null] + | +- OpenStatement[@CanonicalImage = null] + | +- Expression[@CanonicalImage = "CV", @Image = "cv"] + | | +- PrimaryPrefix[@CanonicalImage = "CV", @Image = "cv", @SelfModifier = "false"] + | | +- SimpleExpression[@CanonicalImage = "CV", @Image = "cv"] + | | +- Column[@CanonicalImage = "CV", @Image = "cv"] + | | +- ID[@CanonicalImage = "CV", @Image = "cv"] + | +- SelectStatement[@All = "false", @CanonicalImage = null, @Distinct = "false", @Unique = "false"] + | +- SelectList[@CanonicalImage = null] + | | +- SqlExpression[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | | | +- PrimaryPrefix[@CanonicalImage = "LAST_NAME", @Image = "last_name", @SelfModifier = "false"] + | | | +- SimpleExpression[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | | | +- Column[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | | | +- ID[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | | +- SqlExpression[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | | +- PrimaryPrefix[@CanonicalImage = "JOB_ID", @Image = "job_id", @SelfModifier = "false"] + | | +- SimpleExpression[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | | +- Column[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | | +- ID[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | +- FromClause[@CanonicalImage = null] + | | +- TableReference[@CanonicalImage = null] + | | +- TableName[@CanonicalImage = "EMPLOYEES", @Image = "employees"] + | | +- ID[@CanonicalImage = "EMPLOYEES", @Image = "employees"] + | +- WhereClause[@CanonicalImage = null] + | | +- Condition[@CanonicalImage = null] + | | +- CompoundCondition[@CanonicalImage = null, @Type = null] + | | +- RegexpLikeCondition[@CanonicalImage = null, @MatchParam = null] + | | +- SqlExpression[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | | | +- PrimaryPrefix[@CanonicalImage = "JOB_ID", @Image = "job_id", @SelfModifier = "false"] + | | | +- SimpleExpression[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | | | +- Column[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | | | +- ID[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | | +- SqlExpression[@CanonicalImage = "\'S[HT]_CLERK\'", @Image = "\'S[HT]_CLERK\'"] + | | +- PrimaryPrefix[@CanonicalImage = "\'S[HT]_CLERK\'", @Image = "\'S[HT]_CLERK\'", @SelfModifier = "false"] + | | +- Literal[@CanonicalImage = "\'S[HT]_CLERK\'", @Image = "\'S[HT]_CLERK\'"] + | | +- StringLiteral[@CanonicalImage = "\'S[HT]_CLERK\'", @Image = "\'S[HT]_CLERK\'", @String = "S[HT]_CLERK"] + | +- OrderByClause[@CanonicalImage = null] + | +- SqlExpression[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | +- PrimaryPrefix[@CanonicalImage = "LAST_NAME", @Image = "last_name", @SelfModifier = "false"] + | +- SimpleExpression[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | +- Column[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | +- ID[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + +- Statement[@CanonicalImage = null] + | +- UnlabelledStatement[@CanonicalImage = null] + | +- LoopStatement[@CanonicalImage = null] + | +- Statement[@CanonicalImage = null] + | | +- UnlabelledStatement[@CanonicalImage = null] + | | +- FetchStatement[@BulkCollect = "false", @CanonicalImage = null, @Limit = "false"] + | | +- QualifiedName[@CanonicalImage = "CV", @Image = "cv"] + | | | +- UnqualifiedID[@CanonicalImage = "CV", @Image = "cv"] + | | +- Expression[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | | +- PrimaryPrefix[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname", @SelfModifier = "false"] + | | | +- SimpleExpression[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | | +- Column[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | | +- ID[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | +- Expression[@CanonicalImage = "V_JOBID", @Image = "v_jobid"] + | | +- PrimaryPrefix[@CanonicalImage = "V_JOBID", @Image = "v_jobid", @SelfModifier = "false"] + | | +- SimpleExpression[@CanonicalImage = "V_JOBID", @Image = "v_jobid"] + | | +- Column[@CanonicalImage = "V_JOBID", @Image = "v_jobid"] + | | +- ID[@CanonicalImage = "V_JOBID", @Image = "v_jobid"] + | +- Statement[@CanonicalImage = null] + | | +- UnlabelledStatement[@CanonicalImage = null] + | | +- ExitStatement[@CanonicalImage = null] + | | +- Expression[@CanonicalImage = "CV%NOTFOUND", @Image = "cv%NOTFOUND"] + | | +- PrimaryPrefix[@CanonicalImage = "CV%NOTFOUND", @Image = "cv%NOTFOUND", @SelfModifier = "false"] + | | +- SimpleExpression[@CanonicalImage = "CV%NOTFOUND", @Image = "cv%NOTFOUND"] + | | +- Column[@CanonicalImage = "CV", @Image = "cv"] + | | +- ID[@CanonicalImage = "CV", @Image = "cv"] + | +- Statement[@CanonicalImage = null] + | +- UnlabelledStatement[@CanonicalImage = null] + | +- Expression[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE"] + | +- PrimaryPrefix[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE", @SelfModifier = "false"] + | +- FunctionCall[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE"] + | +- FunctionName[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE"] + | | +- ID[@CanonicalImage = "DBMS_OUTPUT", @Image = "DBMS_OUTPUT"] + | | +- ID[@CanonicalImage = "PUT_LINE", @Image = "PUT_LINE"] + | +- Arguments[@ArgumentCount = "1", @CanonicalImage = null] + | +- ArgumentList[@CanonicalImage = null] + | +- Argument[@CanonicalImage = null] + | +- Expression[@CanonicalImage = "RPAD || V_JOBID", @Image = "RPAD || v_jobid"] + | +- AdditiveExpression[@CanonicalImage = "RPAD || V_JOBID", @Image = "RPAD || v_jobid"] + | +- PrimaryPrefix[@CanonicalImage = "RPAD", @Image = "RPAD", @SelfModifier = "false"] + | | +- FunctionCall[@CanonicalImage = "RPAD", @Image = "RPAD"] + | | +- FunctionName[@CanonicalImage = "RPAD", @Image = "RPAD"] + | | | +- ID[@CanonicalImage = "RPAD", @Image = "RPAD"] + | | +- Arguments[@ArgumentCount = "3", @CanonicalImage = null] + | | +- ArgumentList[@CanonicalImage = null] + | | +- Argument[@CanonicalImage = null] + | | | +- Expression[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | | +- PrimaryPrefix[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname", @SelfModifier = "false"] + | | | +- SimpleExpression[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | | +- Column[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | | +- ID[@CanonicalImage = "V_LASTNAME", @Image = "v_lastname"] + | | +- Argument[@CanonicalImage = null] + | | | +- Expression[@CanonicalImage = "25", @Image = "25"] + | | | +- PrimaryPrefix[@CanonicalImage = "25", @Image = "25", @SelfModifier = "false"] + | | | +- Literal[@CanonicalImage = "25", @Image = "25"] + | | | +- NumericLiteral[@CanonicalImage = "25", @Image = "25"] + | | +- Argument[@CanonicalImage = null] + | | +- Expression[@CanonicalImage = "\' \'", @Image = "\' \'"] + | | +- PrimaryPrefix[@CanonicalImage = "\' \'", @Image = "\' \'", @SelfModifier = "false"] + | | +- Literal[@CanonicalImage = "\' \'", @Image = "\' \'"] + | +- PrimaryPrefix[@CanonicalImage = "V_JOBID", @Image = "v_jobid", @SelfModifier = "false"] + | +- SimpleExpression[@CanonicalImage = "V_JOBID", @Image = "v_jobid"] + | +- Column[@CanonicalImage = "V_JOBID", @Image = "v_jobid"] + | +- ID[@CanonicalImage = "V_JOBID", @Image = "v_jobid"] + +- Statement[@CanonicalImage = null] + | +- UnlabelledStatement[@CanonicalImage = null] + | +- Expression[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE"] + | +- PrimaryPrefix[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE", @SelfModifier = "false"] + | +- FunctionCall[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE"] + | +- FunctionName[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE"] + | | +- ID[@CanonicalImage = "DBMS_OUTPUT", @Image = "DBMS_OUTPUT"] + | | +- ID[@CanonicalImage = "PUT_LINE", @Image = "PUT_LINE"] + | +- Arguments[@ArgumentCount = "1", @CanonicalImage = null] + | +- ArgumentList[@CanonicalImage = null] + | +- Argument[@CanonicalImage = null] + | +- Expression[@CanonicalImage = "\'-------------------------------------\'", @Image = "\'-------------------------------------\'"] + | +- PrimaryPrefix[@CanonicalImage = "\'-------------------------------------\'", @Image = "\'-------------------------------------\'", @SelfModifier = "false"] + | +- Literal[@CanonicalImage = "\'-------------------------------------\'", @Image = "\'-------------------------------------\'"] + | +- StringLiteral[@CanonicalImage = "\'-------------------------------------\'", @Image = "\'-------------------------------------\'", @String = "-------------------------------------"] + +- Statement[@CanonicalImage = null] + | +- UnlabelledStatement[@CanonicalImage = null] + | +- OpenStatement[@CanonicalImage = null] + | +- Expression[@CanonicalImage = "CV", @Image = "cv"] + | | +- PrimaryPrefix[@CanonicalImage = "CV", @Image = "cv", @SelfModifier = "false"] + | | +- SimpleExpression[@CanonicalImage = "CV", @Image = "cv"] + | | +- Column[@CanonicalImage = "CV", @Image = "cv"] + | | +- ID[@CanonicalImage = "CV", @Image = "cv"] + | +- Expression[@CanonicalImage = "QUERY_2", @Image = "query_2"] + | +- PrimaryPrefix[@CanonicalImage = "QUERY_2", @Image = "query_2", @SelfModifier = "false"] + | +- SimpleExpression[@CanonicalImage = "QUERY_2", @Image = "query_2"] + | +- Column[@CanonicalImage = "QUERY_2", @Image = "query_2"] + | +- ID[@CanonicalImage = "QUERY_2", @Image = "query_2"] + +- Statement[@CanonicalImage = null] + | +- UnlabelledStatement[@CanonicalImage = null] + | +- LoopStatement[@CanonicalImage = null] + | +- Statement[@CanonicalImage = null] + | | +- UnlabelledStatement[@CanonicalImage = null] + | | +- FetchStatement[@BulkCollect = "false", @CanonicalImage = null, @Limit = "false"] + | | +- QualifiedName[@CanonicalImage = "CV", @Image = "cv"] + | | | +- UnqualifiedID[@CanonicalImage = "CV", @Image = "cv"] + | | +- Expression[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | | +- PrimaryPrefix[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees", @SelfModifier = "false"] + | | +- SimpleExpression[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | | +- Column[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | | +- ID[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | +- Statement[@CanonicalImage = null] + | | +- UnlabelledStatement[@CanonicalImage = null] + | | +- ExitStatement[@CanonicalImage = null] + | | +- Expression[@CanonicalImage = "CV%NOTFOUND", @Image = "cv%NOTFOUND"] + | | +- PrimaryPrefix[@CanonicalImage = "CV%NOTFOUND", @Image = "cv%NOTFOUND", @SelfModifier = "false"] + | | +- SimpleExpression[@CanonicalImage = "CV%NOTFOUND", @Image = "cv%NOTFOUND"] + | | +- Column[@CanonicalImage = "CV", @Image = "cv"] + | | +- ID[@CanonicalImage = "CV", @Image = "cv"] + | +- Statement[@CanonicalImage = null] + | +- UnlabelledStatement[@CanonicalImage = null] + | +- Expression[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE"] + | +- PrimaryPrefix[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE", @SelfModifier = "false"] + | +- FunctionCall[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE"] + | +- FunctionName[@CanonicalImage = "DBMS_OUTPUT.PUT_LINE", @Image = "DBMS_OUTPUT.PUT_LINE"] + | | +- ID[@CanonicalImage = "DBMS_OUTPUT", @Image = "DBMS_OUTPUT"] + | | +- ID[@CanonicalImage = "PUT_LINE", @Image = "PUT_LINE"] + | +- Arguments[@ArgumentCount = "1", @CanonicalImage = null] + | +- ArgumentList[@CanonicalImage = null] + | +- Argument[@CanonicalImage = null] + | +- Expression[@CanonicalImage = "RPAD || V_EMPLOYEES.JOB_ID", @Image = "RPAD || v_employees.job_id"] + | +- AdditiveExpression[@CanonicalImage = "RPAD || V_EMPLOYEES.JOB_ID", @Image = "RPAD || v_employees.job_id"] + | +- PrimaryPrefix[@CanonicalImage = "RPAD", @Image = "RPAD", @SelfModifier = "false"] + | | +- FunctionCall[@CanonicalImage = "RPAD", @Image = "RPAD"] + | | +- FunctionName[@CanonicalImage = "RPAD", @Image = "RPAD"] + | | | +- ID[@CanonicalImage = "RPAD", @Image = "RPAD"] + | | +- Arguments[@ArgumentCount = "3", @CanonicalImage = null] + | | +- ArgumentList[@CanonicalImage = null] + | | +- Argument[@CanonicalImage = null] + | | | +- Expression[@CanonicalImage = "V_EMPLOYEES.LAST_NAME", @Image = "v_employees.last_name"] + | | | +- PrimaryPrefix[@CanonicalImage = "V_EMPLOYEES.LAST_NAME", @Image = "v_employees.last_name", @SelfModifier = "false"] + | | | +- SimpleExpression[@CanonicalImage = "V_EMPLOYEES.LAST_NAME", @Image = "v_employees.last_name"] + | | | +- TableName[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | | | | +- ID[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | | | +- Column[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | | | +- ID[@CanonicalImage = "LAST_NAME", @Image = "last_name"] + | | +- Argument[@CanonicalImage = null] + | | | +- Expression[@CanonicalImage = "25", @Image = "25"] + | | | +- PrimaryPrefix[@CanonicalImage = "25", @Image = "25", @SelfModifier = "false"] + | | | +- Literal[@CanonicalImage = "25", @Image = "25"] + | | | +- NumericLiteral[@CanonicalImage = "25", @Image = "25"] + | | +- Argument[@CanonicalImage = null] + | | +- Expression[@CanonicalImage = "\' \'", @Image = "\' \'"] + | | +- PrimaryPrefix[@CanonicalImage = "\' \'", @Image = "\' \'", @SelfModifier = "false"] + | | +- Literal[@CanonicalImage = "\' \'", @Image = "\' \'"] + | +- PrimaryPrefix[@CanonicalImage = "V_EMPLOYEES.JOB_ID", @Image = "v_employees.job_id", @SelfModifier = "false"] + | +- SimpleExpression[@CanonicalImage = "V_EMPLOYEES.JOB_ID", @Image = "v_employees.job_id"] + | +- TableName[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | | +- ID[@CanonicalImage = "V_EMPLOYEES", @Image = "v_employees"] + | +- Column[@CanonicalImage = "JOB_ID", @Image = "job_id"] + | +- ID[@CanonicalImage = "JOB_ID", @Image = "job_id"] + +- Statement[@CanonicalImage = null] + +- UnlabelledStatement[@CanonicalImage = null] + +- CloseStatement[@CanonicalImage = "CV", @Image = "cv"] + +- QualifiedName[@CanonicalImage = "CV", @Image = "cv"] + +- UnqualifiedID[@CanonicalImage = "CV", @Image = "cv"] diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SelectIntoArray.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SelectIntoArray.pls new file mode 100644 index 0000000000..b8698f8422 --- /dev/null +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SelectIntoArray.pls @@ -0,0 +1,22 @@ +-- +-- See https://github.com/pmd/pmd/issues/3515 +-- + +CREATE OR REPLACE PROCEDURE EXAMPLE_PROCEDURE IS + -- + TYPE example_data_rt IS RECORD( + field_one PLS_INTEGER, + field_two PLS_INTEGER, + field_three PLS_INTEGER); + -- + TYPE example_data_aat IS TABLE OF example_data_rt INDEX BY BINARY_INTEGER; + -- + l_example_data example_data_aat; + -- +BEGIN + -- + SELECT 1 field_value_one, 2 field_value_two, 3 field_value_three + INTO l_example_data(1).field_one,l_example_data(1).field_two,l_example_data(1).field_three + FROM DUAL; + -- +END EXAMPLE_PROCEDURE; diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SelectIntoArray.txt b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SelectIntoArray.txt new file mode 100644 index 0000000000..58f0ed86a6 --- /dev/null +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/SelectIntoArray.txt @@ -0,0 +1,81 @@ ++- Input[@CanonicalImage = null, @ExcludedLinesCount = "0", @ExcludedRangesCount = "0"] + +- Global[@CanonicalImage = null] + +- ProgramUnit[@CanonicalImage = null, @MethodName = "EXAMPLE_PROCEDURE", @Name = "EXAMPLE_PROCEDURE", @ObjectName = null] + +- MethodDeclarator[@CanonicalImage = "EXAMPLE_PROCEDURE", @Image = "EXAMPLE_PROCEDURE", @ParameterCount = "1"] + | +- ObjectNameDeclaration[@CanonicalImage = "EXAMPLE_PROCEDURE", @Image = "EXAMPLE_PROCEDURE"] + | +- ID[@CanonicalImage = "EXAMPLE_PROCEDURE", @Image = "EXAMPLE_PROCEDURE"] + +- DeclarativeSection[@CanonicalImage = null] + | +- DeclarativeUnit[@CanonicalImage = null] + | | +- SubTypeDefinition[@CanonicalImage = "EXAMPLE_DATA_RT", @Image = "example_data_rt"] + | | +- QualifiedID[@CanonicalImage = "EXAMPLE_DATA_RT", @Image = "example_data_rt"] + | | +- FieldDeclaration[@CanonicalImage = "FIELD_ONE", @Image = "field_one"] + | | | +- ID[@CanonicalImage = "FIELD_ONE", @Image = "field_one"] + | | | +- Datatype[@CanonicalImage = "PLS_INTEGER", @Image = "PLS_INTEGER", @TypeImage = "PLS_INTEGER"] + | | | +- ScalarDataTypeName[@CanonicalImage = "PLS_INTEGER", @Image = "PLS_INTEGER"] + | | +- FieldDeclaration[@CanonicalImage = "FIELD_TWO", @Image = "field_two"] + | | | +- ID[@CanonicalImage = "FIELD_TWO", @Image = "field_two"] + | | | +- Datatype[@CanonicalImage = "PLS_INTEGER", @Image = "PLS_INTEGER", @TypeImage = "PLS_INTEGER"] + | | | +- ScalarDataTypeName[@CanonicalImage = "PLS_INTEGER", @Image = "PLS_INTEGER"] + | | +- FieldDeclaration[@CanonicalImage = "FIELD_THREE", @Image = "field_three"] + | | +- ID[@CanonicalImage = "FIELD_THREE", @Image = "field_three"] + | | +- Datatype[@CanonicalImage = "PLS_INTEGER", @Image = "PLS_INTEGER", @TypeImage = "PLS_INTEGER"] + | | +- ScalarDataTypeName[@CanonicalImage = "PLS_INTEGER", @Image = "PLS_INTEGER"] + | +- DeclarativeUnit[@CanonicalImage = null] + | | +- SubTypeDefinition[@CanonicalImage = "EXAMPLE_DATA_AAT", @Image = "example_data_aat"] + | | +- QualifiedID[@CanonicalImage = "EXAMPLE_DATA_AAT", @Image = "example_data_aat"] + | | +- Datatype[@CanonicalImage = "EXAMPLE_DATA_RT", @Image = "example_data_rt", @TypeImage = "example_data_rt"] + | | | +- QualifiedName[@CanonicalImage = "EXAMPLE_DATA_RT", @Image = "example_data_rt"] + | | | +- UnqualifiedID[@CanonicalImage = "EXAMPLE_DATA_RT", @Image = "example_data_rt"] + | | +- Datatype[@CanonicalImage = "BINARY_INTEGER", @Image = "BINARY_INTEGER", @TypeImage = "BINARY_INTEGER"] + | | +- ScalarDataTypeName[@CanonicalImage = "BINARY_INTEGER", @Image = "BINARY_INTEGER"] + | +- DeclarativeUnit[@CanonicalImage = null] + | +- VariableOrConstantDeclaration[@CanonicalImage = null] + | +- VariableOrConstantDeclarator[@CanonicalImage = "L_EXAMPLE_DATA EXAMPLE_DATA_AAT", @Image = "l_example_data example_data_aat"] + | +- VariableOrConstantDeclaratorId[@Array = "false", @ArrayDepth = "0", @CanonicalImage = "L_EXAMPLE_DATA", @Image = "l_example_data"] + | | +- ID[@CanonicalImage = "L_EXAMPLE_DATA", @Image = "l_example_data"] + | +- Datatype[@CanonicalImage = "EXAMPLE_DATA_AAT", @Image = "example_data_aat", @TypeImage = "example_data_aat"] + | +- QualifiedName[@CanonicalImage = "EXAMPLE_DATA_AAT", @Image = "example_data_aat"] + | +- UnqualifiedID[@CanonicalImage = "EXAMPLE_DATA_AAT", @Image = "example_data_aat"] + +- Statement[@CanonicalImage = null] + | +- UnlabelledStatement[@CanonicalImage = null] + | +- SelectIntoStatement[@All = "false", @CanonicalImage = null, @Distinct = "false", @Unique = "false"] + | +- SelectList[@CanonicalImage = null] + | | +- SqlExpression[@CanonicalImage = "1", @Image = "1"] + | | | +- PrimaryPrefix[@CanonicalImage = "1", @Image = "1", @SelfModifier = "false"] + | | | +- Literal[@CanonicalImage = "1", @Image = "1"] + | | | +- NumericLiteral[@CanonicalImage = "1", @Image = "1"] + | | +- ColumnAlias[@CanonicalImage = "FIELD_VALUE_ONE", @Image = "field_value_one"] + | | | +- ID[@CanonicalImage = "FIELD_VALUE_ONE", @Image = "field_value_one"] + | | +- SqlExpression[@CanonicalImage = "2", @Image = "2"] + | | | +- PrimaryPrefix[@CanonicalImage = "2", @Image = "2", @SelfModifier = "false"] + | | | +- Literal[@CanonicalImage = "2", @Image = "2"] + | | | +- NumericLiteral[@CanonicalImage = "2", @Image = "2"] + | | +- ColumnAlias[@CanonicalImage = "FIELD_VALUE_TWO", @Image = "field_value_two"] + | | | +- ID[@CanonicalImage = "FIELD_VALUE_TWO", @Image = "field_value_two"] + | | +- SqlExpression[@CanonicalImage = "3", @Image = "3"] + | | | +- PrimaryPrefix[@CanonicalImage = "3", @Image = "3", @SelfModifier = "false"] + | | | +- Literal[@CanonicalImage = "3", @Image = "3"] + | | | +- NumericLiteral[@CanonicalImage = "3", @Image = "3"] + | | +- ColumnAlias[@CanonicalImage = "FIELD_VALUE_THREE", @Image = "field_value_three"] + | | +- ID[@CanonicalImage = "FIELD_VALUE_THREE", @Image = "field_value_three"] + | +- IntoClause[@CanonicalImage = null] + | | +- VariableName[@CanonicalImage = "L_EXAMPLE_DATA(1).FIELD_ONE", @Image = "l_example_data(1).field_one"] + | | | +- ID[@CanonicalImage = "L_EXAMPLE_DATA", @Image = "l_example_data"] + | | | +- Literal[@CanonicalImage = "1", @Image = "1"] + | | | | +- NumericLiteral[@CanonicalImage = "1", @Image = "1"] + | | | +- ID[@CanonicalImage = "FIELD_ONE", @Image = "field_one"] + | | +- VariableName[@CanonicalImage = "L_EXAMPLE_DATA(1).FIELD_TWO", @Image = "l_example_data(1).field_two"] + | | | +- ID[@CanonicalImage = "L_EXAMPLE_DATA", @Image = "l_example_data"] + | | | +- Literal[@CanonicalImage = "1", @Image = "1"] + | | | | +- NumericLiteral[@CanonicalImage = "1", @Image = "1"] + | | | +- ID[@CanonicalImage = "FIELD_TWO", @Image = "field_two"] + | | +- VariableName[@CanonicalImage = "L_EXAMPLE_DATA(1).FIELD_THREE", @Image = "l_example_data(1).field_three"] + | | +- ID[@CanonicalImage = "L_EXAMPLE_DATA", @Image = "l_example_data"] + | | +- Literal[@CanonicalImage = "1", @Image = "1"] + | | | +- NumericLiteral[@CanonicalImage = "1", @Image = "1"] + | | +- ID[@CanonicalImage = "FIELD_THREE", @Image = "field_three"] + | +- FromClause[@CanonicalImage = null] + | +- TableReference[@CanonicalImage = null] + | +- TableName[@CanonicalImage = "DUAL", @Image = "DUAL"] + | +- ID[@CanonicalImage = "DUAL", @Image = "DUAL"] + +- ID[@CanonicalImage = "EXAMPLE_PROCEDURE", @Image = "EXAMPLE_PROCEDURE"] diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/Using.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/Using.pls index 3eaf1734f7..57f419eb5e 100644 --- a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/Using.pls +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/Using.pls @@ -12,5 +12,5 @@ BEGIN open cursor for 'query' USING variable; open cursor for 'query' USING IN variable; open cursor for 'query' USING OUT variable, IN othervariable; - open cursor for 'query' USING IN_OUT variable; + open cursor for 'query' USING IN OUT variable; END; diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java index e36a3c0af7..6dff8fbf56 100644 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java +++ b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java @@ -21,6 +21,6 @@ public class SwiftLanguageModule extends BaseLanguageModule { */ public SwiftLanguageModule() { super(NAME, null, TERSE_NAME, "swift"); - addVersion("", new SwiftHandler(), true); + addDefaultVersion("", new SwiftHandler()); } } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCLITest.java b/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCLITest.java index 1c8645fc78..8bf16ce468 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCLITest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCLITest.java @@ -94,7 +94,7 @@ public abstract class BaseCLITest { protected void checkStatusCode(int expectedExitCode) { int statusCode = getStatusCode(); if (statusCode != expectedExitCode) { - fail("PMD failed with status code:" + statusCode); + fail("PMD failed with status code: " + statusCode); } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/AbstractVfRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/AbstractVfRule.java index 5654b08438..23cef3e9f0 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/AbstractVfRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/AbstractVfRule.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -21,5 +21,4 @@ public abstract class AbstractVfRule extends AbstractRule implements VfParserVis public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); } - } diff --git a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/AbstractVmRule.java b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/AbstractVmRule.java index 33c7a057e2..9892992abd 100644 --- a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/AbstractVmRule.java +++ b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/AbstractVmRule.java @@ -21,5 +21,4 @@ public abstract class AbstractVmRule extends AbstractRule implements VmParserVis public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); } - } diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java new file mode 100644 index 0000000000..a3c0412a83 --- /dev/null +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlCliTest.java @@ -0,0 +1,66 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.xml; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.cli.BaseCLITest; + +public class XmlCliTest extends BaseCLITest { + private static final String BASE_DIR = "src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject"; + private static final String RULE_MESSAGE = "A tags are not allowed"; + + private String[] createArgs(String directory, String ... args) { + List arguments = new ArrayList<>(); + arguments.add("-f"); + arguments.add("text"); + arguments.add("-no-cache"); + arguments.add("-R"); + arguments.add(BASE_DIR + "/ruleset.xml"); + arguments.add("-d"); + arguments.add(BASE_DIR + directory); + arguments.addAll(Arrays.asList(args)); + return arguments.toArray(new String[0]); + } + + @Test + public void analyzeSingleXmlWithoutForceLanguage() { + String resultFilename = runTest(createArgs("/src/file1.ext"), "analyzeSingleXmlWithoutForceLanguage", 0); + assertRuleMessage(0, resultFilename); + } + + @Test + public void analyzeSingleXmlWithForceLanguage() { + String resultFilename = runTest(createArgs("/src/file1.ext", "-force-language", "xml"), + "analyzeSingleXmlWithForceLanguage", 4); + assertRuleMessage(1, resultFilename); + } + + @Test + public void analyzeDirectoryWithForceLanguage() { + String resultFilename = runTest(createArgs("/src/", "-force-language", "xml"), + "analyzeDirectoryWithForceLanguage", 4); + assertRuleMessage(3, resultFilename); + } + + private void assertRuleMessage(int expectedCount, String resultFilename) { + try { + String result = FileUtils.readFileToString(new File(resultFilename), StandardCharsets.UTF_8); + Assert.assertEquals(expectedCount, StringUtils.countMatches(result, RULE_MESSAGE)); + } catch (IOException e) { + throw new AssertionError(e); + } + } +} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/XmlXPathRuleTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/XmlXPathRuleTest.java new file mode 100644 index 0000000000..ef22aa88d8 --- /dev/null +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/XmlXPathRuleTest.java @@ -0,0 +1,39 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.xml.rule; + +import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertSize; + +import org.junit.Test; + +import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.rule.XPathRule; +import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; +import net.sourceforge.pmd.lang.xml.XmlLanguageModule; +import net.sourceforge.pmd.lang.xml.XmlParsingHelper; + +public class XmlXPathRuleTest { + + final XmlParsingHelper xml = XmlParsingHelper.XML; + + private XPathRule makeXPath(String expression) { + XPathRule rule = new XPathRule(XPathVersion.XPATH_2_0, expression); + rule.setLanguage(LanguageRegistry.getLanguage(XmlLanguageModule.NAME)); + rule.setMessage("XPath Rule Failed"); + return rule; + } + + + @Test + public void testFileNameInXpath() { + Report report = xml.executeRule(makeXPath("//b[pmd:fileName() = 'Foo.xml']"), + "", + "src/Foo.xml"); + + assertSize(report, 1); + } + +} diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/ruleset.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/ruleset.xml new file mode 100644 index 0000000000..4f3cdbfd95 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/ruleset.xml @@ -0,0 +1,31 @@ + + + + + +Sample + + + + +A tags are not allowed + + 3 + + + + + + + + + + diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file1.ext b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file1.ext new file mode 100644 index 0000000000..70c5b81e76 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file1.ext @@ -0,0 +1,5 @@ + + + + + diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file2.ext b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file2.ext new file mode 100644 index 0000000000..1e53662113 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file2.ext @@ -0,0 +1,6 @@ + + + + + + diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file3.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file3.txt new file mode 100644 index 0000000000..f9b28b06e9 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file3.txt @@ -0,0 +1,3 @@ +BSD-style license; for more info see http://pmd.sourceforge.net/license.html + +Other file that is not a xml file. diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file4.ext b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file4.ext new file mode 100644 index 0000000000..bab5e698dc --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject/src/file4.ext @@ -0,0 +1,4 @@ + + + + diff --git a/pom.xml b/pom.xml index c6cc4bec6f..42964fe379 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ - 2021-06-26T08:24:14Z + 2021-11-27T10:21:16Z 8 @@ -94,8 +94,8 @@ 3.0.0-M5 8.42 3.1.2 - 3.14.0 - 1.10.9 + 3.15.0 + 1.10.12 3.2.0 4.8 @@ -105,9 +105,9 @@ -Xmx512m -Dfile.encoding=${project.build.sourceEncoding} - 14 + 18-SNAPSHOT - 6.27.0 + 6.37.0 ${settings.localRepository}/net/java/dev/javacc/javacc/${javacc.version}/javacc-${javacc.version}.jar ${project.build.directory}/generated-sources/javacc ${project.basedir}/../javacc-wrapper.xml @@ -390,7 +390,7 @@ verify check - cpd + cpd-check @@ -412,15 +412,16 @@ + net.sourceforge.pmd pmd-core - 6.35.0 + 6.41.0 net.sourceforge.pmd pmd-java - 6.35.0 + 6.41.0 @@ -660,7 +661,7 @@ org.ow2.asm asm - 9.1 + 9.2 org.pcollections @@ -801,6 +802,12 @@ system-rules 1.19.0 test + + + junit + junit-dep + +