Merge branch 'pmd/7.0.x' into cannot-resolve-ambiguous-dollar

This commit is contained in:
Clément Fournier
2022-03-10 13:52:54 +02:00
committed by GitHub
68 changed files with 3015 additions and 951 deletions

View File

@ -6539,6 +6539,15 @@
"code"
]
},
{
"login": "filiprafalowicz",
"name": "filiprafalowicz",
"avatar_url": "https://avatars.githubusercontent.com/u/24355557?v=4",
"profile": "https://github.com/filiprafalowicz",
"contributions": [
"code"
]
},
{
"login": "JerritEic",
"name": "JerritEic",

View File

@ -23,7 +23,7 @@ jobs:
os: [ ubuntu-latest, windows-latest, macos-latest ]
if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 2
- uses: actions/cache@v2

View File

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
continue-on-error: false
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 100
- name: Setup Environment

View File

@ -12,7 +12,7 @@ jobs:
os: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: actions/cache@v2
with:
path: |

View File

@ -775,163 +775,164 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="https://github.com/ekkirala"><img src="https://avatars.githubusercontent.com/u/44954455?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ekkirala</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aekkirala" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/emersonmoura"><img src="https://avatars.githubusercontent.com/u/5419868?v=4?s=100" width="100px;" alt=""/><br /><sub><b>emersonmoura</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aemersonmoura" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://juejin.cn/user/1063982985642525"><img src="https://avatars.githubusercontent.com/u/24585054?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fairy</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aguxiaonian" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/filiprafalowicz"><img src="https://avatars.githubusercontent.com/u/24355557?v=4?s=100" width="100px;" alt=""/><br /><sub><b>filiprafalowicz</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=filiprafalowicz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/foxmason"><img src="https://avatars.githubusercontent.com/u/33361071?v=4?s=100" width="100px;" alt=""/><br /><sub><b>foxmason</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Afoxmason" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/frankegabor"><img src="https://avatars.githubusercontent.com/u/13273444?v=4?s=100" width="100px;" alt=""/><br /><sub><b>frankegabor</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Afrankegabor" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/frankegabor"><img src="https://avatars.githubusercontent.com/u/13273444?v=4?s=100" width="100px;" alt=""/><br /><sub><b>frankegabor</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Afrankegabor" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/fanlw0816"><img src="https://avatars.githubusercontent.com/u/22781995?v=4?s=100" width="100px;" alt=""/><br /><sub><b>frankl</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Afanlw0816" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/freafrea"><img src="https://avatars.githubusercontent.com/u/39403091?v=4?s=100" width="100px;" alt=""/><br /><sub><b>freafrea</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Afreafrea" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/fsapatin"><img src="https://avatars.githubusercontent.com/u/10675254?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fsapatin</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Afsapatin" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/gracia19"><img src="https://avatars.githubusercontent.com/u/32557952?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gracia19</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Agracia19" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/ief2009"><img src="https://avatars.githubusercontent.com/u/1955449?v=4?s=100" width="100px;" alt=""/><br /><sub><b>guo fei</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aief2009" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/gurmsc5"><img src="https://avatars.githubusercontent.com/u/26914263?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gurmsc5</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Agurmsc5" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/gwilymatgearset"><img src="https://avatars.githubusercontent.com/u/43957113?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gwilymatgearset</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=gwilymatgearset" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Agwilymatgearset" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/gwilymatgearset"><img src="https://avatars.githubusercontent.com/u/43957113?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gwilymatgearset</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=gwilymatgearset" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Agwilymatgearset" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/haigsn"><img src="https://avatars.githubusercontent.com/u/52993319?v=4?s=100" width="100px;" alt=""/><br /><sub><b>haigsn</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ahaigsn" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/hemanshu070"><img src="https://avatars.githubusercontent.com/u/32012651?v=4?s=100" width="100px;" alt=""/><br /><sub><b>hemanshu070</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ahemanshu070" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/henrik242"><img src="https://avatars.githubusercontent.com/u/129931?v=4?s=100" width="100px;" alt=""/><br /><sub><b>henrik242</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ahenrik242" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/hongpuwu"><img src="https://avatars.githubusercontent.com/u/19198552?v=4?s=100" width="100px;" alt=""/><br /><sub><b>hongpuwu</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ahongpuwu" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/hvbtup"><img src="https://avatars.githubusercontent.com/u/7644776?v=4?s=100" width="100px;" alt=""/><br /><sub><b>hvbtup</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=hvbtup" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Ahvbtup" title="Bug reports">🐛</a></td>
<td align="center"><a href="http://www.igniti.de/"><img src="https://avatars.githubusercontent.com/u/7207145?v=4?s=100" width="100px;" alt=""/><br /><sub><b>igniti GmbH</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aigniti-gmbh" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/ilovezfs"><img src="https://avatars.githubusercontent.com/u/5268928?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ilovezfs</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ailovezfs" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/ilovezfs"><img src="https://avatars.githubusercontent.com/u/5268928?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ilovezfs</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ailovezfs" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/itaigilo"><img src="https://avatars.githubusercontent.com/u/13402361?v=4?s=100" width="100px;" alt=""/><br /><sub><b>itaigilo</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aitaigilo" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/jakivey32"><img src="https://avatars.githubusercontent.com/u/36869603?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jakivey32</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ajakivey32" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/jbennett2091"><img src="https://avatars.githubusercontent.com/u/16721671?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jbennett2091</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ajbennett2091" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/jcamerin"><img src="https://avatars.githubusercontent.com/u/7663252?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jcamerin</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ajcamerin" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/jkeener1"><img src="https://avatars.githubusercontent.com/u/11696155?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jkeener1</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ajkeener1" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/jmetertea"><img src="https://avatars.githubusercontent.com/u/33323555?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jmetertea</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ajmetertea" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/johnra2"><img src="https://avatars.githubusercontent.com/u/90150885?v=4?s=100" width="100px;" alt=""/><br /><sub><b>johnra2</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=johnra2" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/johnra2"><img src="https://avatars.githubusercontent.com/u/90150885?v=4?s=100" width="100px;" alt=""/><br /><sub><b>johnra2</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=johnra2" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/josemanuelrolon"><img src="https://avatars.githubusercontent.com/u/1685807?v=4?s=100" width="100px;" alt=""/><br /><sub><b>josemanuelrolon</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=josemanuelrolon" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Ajosemanuelrolon" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/kabroxiko"><img src="https://avatars.githubusercontent.com/u/20568120?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kabroxiko</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=kabroxiko" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Akabroxiko" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/karwer"><img src="https://avatars.githubusercontent.com/u/862540?v=4?s=100" width="100px;" alt=""/><br /><sub><b>karwer</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Akarwer" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/kaulonline"><img src="https://avatars.githubusercontent.com/u/1171723?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kaulonline</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Akaulonline" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/kdaemonv"><img src="https://avatars.githubusercontent.com/u/5984651?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kdaemonv</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Akdaemonv" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/kenji21"><img src="https://avatars.githubusercontent.com/u/1105089?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kenji21</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=kenji21" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Akenji21" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/kfranic"><img src="https://avatars.githubusercontent.com/u/26544594?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kfranic</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Akfranic" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/kfranic"><img src="https://avatars.githubusercontent.com/u/26544594?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kfranic</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Akfranic" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/khalidkh"><img src="https://avatars.githubusercontent.com/u/6832066?v=4?s=100" width="100px;" alt=""/><br /><sub><b>khalidkh</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Akhalidkh" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/krzyk"><img src="https://avatars.githubusercontent.com/u/105730?v=4?s=100" width="100px;" alt=""/><br /><sub><b>krzyk</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Akrzyk" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/lasselindqvist"><img src="https://avatars.githubusercontent.com/u/13466645?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lasselindqvist</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Alasselindqvist" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/lihuaib"><img src="https://avatars.githubusercontent.com/u/3365643?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lihuaib</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Alihuaib" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/lonelyma1021"><img src="https://avatars.githubusercontent.com/u/22359014?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lonelyma1021</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Alonelyma1021" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/lpeddy"><img src="https://avatars.githubusercontent.com/u/48803108?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lpeddy</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Alpeddy" title="Bug reports">🐛</a></td>
<td align="center"><a href="http://lujie.ac.cn/"><img src="https://avatars.githubusercontent.com/u/2918158?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lujiefsi</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=lujiefsi" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://lujie.ac.cn/"><img src="https://avatars.githubusercontent.com/u/2918158?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lujiefsi</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=lujiefsi" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lyriccoder"><img src="https://avatars.githubusercontent.com/u/20803206?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lyriccoder</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Alyriccoder" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/marcelmore"><img src="https://avatars.githubusercontent.com/u/2975481?v=4?s=100" width="100px;" alt=""/><br /><sub><b>marcelmore</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amarcelmore" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/matchboxy"><img src="https://avatars.githubusercontent.com/u/6457674?v=4?s=100" width="100px;" alt=""/><br /><sub><b>matchbox</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amatchboxy" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/matthiaskraaz"><img src="https://avatars.githubusercontent.com/u/5954500?v=4?s=100" width="100px;" alt=""/><br /><sub><b>matthiaskraaz</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amatthiaskraaz" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/mkeller-ergon"><img src="https://avatars.githubusercontent.com/u/23031669?v=4?s=100" width="100px;" alt=""/><br /><sub><b>meandonlyme</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amkeller-ergon" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/mikesive"><img src="https://avatars.githubusercontent.com/u/4043189?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mikesive</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amikesive" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/milossesic"><img src="https://avatars.githubusercontent.com/u/20756244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>milossesic</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amilossesic" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/milossesic"><img src="https://avatars.githubusercontent.com/u/20756244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>milossesic</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amilossesic" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/mriddell95"><img src="https://avatars.githubusercontent.com/u/25618660?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mriddell95</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amriddell95" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/mrlzh"><img src="https://avatars.githubusercontent.com/u/13222791?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mrlzh</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amrlzh" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/msloan"><img src="https://avatars.githubusercontent.com/u/1783723?v=4?s=100" width="100px;" alt=""/><br /><sub><b>msloan</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amsloan" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/mucharlaravalika"><img src="https://avatars.githubusercontent.com/u/32505587?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mucharlaravalika</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amucharlaravalika" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/mvenneman"><img src="https://avatars.githubusercontent.com/u/1266912?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mvenneman</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amvenneman" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/nareshl119"><img src="https://avatars.githubusercontent.com/u/39321364?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nareshl119</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Anareshl119" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/nicolas-harraudeau-sonarsource"><img src="https://avatars.githubusercontent.com/u/40498978?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nicolas-harraudeau-sonarsource</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Anicolas-harraudeau-sonarsource" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/nicolas-harraudeau-sonarsource"><img src="https://avatars.githubusercontent.com/u/40498978?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nicolas-harraudeau-sonarsource</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Anicolas-harraudeau-sonarsource" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/noerremark"><img src="https://avatars.githubusercontent.com/u/4252411?v=4?s=100" width="100px;" alt=""/><br /><sub><b>noerremark</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Anoerremark" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/novsirion"><img src="https://avatars.githubusercontent.com/u/7797113?v=4?s=100" width="100px;" alt=""/><br /><sub><b>novsirion</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Anovsirion" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/oggboy"><img src="https://avatars.githubusercontent.com/u/4798818?v=4?s=100" width="100px;" alt=""/><br /><sub><b>oggboy</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aoggboy" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://journal.lampetty.net/archive/category/in%20English"><img src="https://avatars.githubusercontent.com/u/78990?v=4?s=100" width="100px;" alt=""/><br /><sub><b>oinume</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aoinume" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/orimarko"><img src="https://avatars.githubusercontent.com/u/17137249?v=4?s=100" width="100px;" alt=""/><br /><sub><b>orimarko</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=orimarko" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Aorimarko" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/pagarwal-ignitetech"><img src="https://avatars.githubusercontent.com/u/30888430?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pallavi agarwal</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Apagarwal-ignitetech" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/parksungrin"><img src="https://avatars.githubusercontent.com/u/29750262?v=4?s=100" width="100px;" alt=""/><br /><sub><b>parksungrin</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aparksungrin" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/parksungrin"><img src="https://avatars.githubusercontent.com/u/29750262?v=4?s=100" width="100px;" alt=""/><br /><sub><b>parksungrin</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aparksungrin" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/patpatpat123"><img src="https://avatars.githubusercontent.com/u/43899031?v=4?s=100" width="100px;" alt=""/><br /><sub><b>patpatpat123</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Apatpatpat123" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/patriksevallius"><img src="https://avatars.githubusercontent.com/u/7291479?v=4?s=100" width="100px;" alt=""/><br /><sub><b>patriksevallius</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Apatriksevallius" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/pbrajesh1"><img src="https://avatars.githubusercontent.com/u/32388299?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pbrajesh1</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Apbrajesh1" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/phoenix384"><img src="https://avatars.githubusercontent.com/u/3883662?v=4?s=100" width="100px;" alt=""/><br /><sub><b>phoenix384</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aphoenix384" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/piotrszymanski-sc"><img src="https://avatars.githubusercontent.com/u/71124942?v=4?s=100" width="100px;" alt=""/><br /><sub><b>piotrszymanski-sc</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=piotrszymanski-sc" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/plan3d"><img src="https://avatars.githubusercontent.com/u/76825073?v=4?s=100" width="100px;" alt=""/><br /><sub><b>plan3d</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aplan3d" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/poojasix"><img src="https://avatars.githubusercontent.com/u/85337280?v=4?s=100" width="100px;" alt=""/><br /><sub><b>poojasix</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Apoojasix" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/poojasix"><img src="https://avatars.githubusercontent.com/u/85337280?v=4?s=100" width="100px;" alt=""/><br /><sub><b>poojasix</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Apoojasix" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/prabhushrikant"><img src="https://avatars.githubusercontent.com/u/6848200?v=4?s=100" width="100px;" alt=""/><br /><sub><b>prabhushrikant</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aprabhushrikant" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/pujitha8783"><img src="https://avatars.githubusercontent.com/u/20646357?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pujitha8783</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Apujitha8783" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/r-r-a-j"><img src="https://avatars.githubusercontent.com/u/33902071?v=4?s=100" width="100px;" alt=""/><br /><sub><b>r-r-a-j</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ar-r-a-j" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/raghujayjunk"><img src="https://avatars.githubusercontent.com/u/48074475?v=4?s=100" width="100px;" alt=""/><br /><sub><b>raghujayjunk</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Araghujayjunk" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/rajeshveera"><img src="https://avatars.githubusercontent.com/u/1306514?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rajeshveera</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Arajeshveera" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/rajeswarreddy88"><img src="https://avatars.githubusercontent.com/u/48543250?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rajeswarreddy88</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Arajeswarreddy88" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/recdevs"><img src="https://avatars.githubusercontent.com/u/63118273?v=4?s=100" width="100px;" alt=""/><br /><sub><b>recdevs</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Arecdevs" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/recdevs"><img src="https://avatars.githubusercontent.com/u/63118273?v=4?s=100" width="100px;" alt=""/><br /><sub><b>recdevs</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Arecdevs" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/reudismam"><img src="https://avatars.githubusercontent.com/u/1970407?v=4?s=100" width="100px;" alt=""/><br /><sub><b>reudismam</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=reudismam" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Areudismam" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/rijkt"><img src="https://avatars.githubusercontent.com/u/56129985?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rijkt</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Arijkt" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/rillig-tk"><img src="https://avatars.githubusercontent.com/u/46376960?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rillig-tk</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Arillig-tk" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/rmohan20"><img src="https://avatars.githubusercontent.com/u/58573547?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rmohan20</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=rmohan20" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Armohan20" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://rxmicro.io/"><img src="https://avatars.githubusercontent.com/u/54791695?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rxmicro</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Arxmicro" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/ryan-gustafson"><img src="https://avatars.githubusercontent.com/u/1227016?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ryan-gustafson</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=ryan-gustafson" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Aryan-gustafson" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/sabi0"><img src="https://avatars.githubusercontent.com/u/11509875?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sabi0</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asabi0" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/sabi0"><img src="https://avatars.githubusercontent.com/u/11509875?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sabi0</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asabi0" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/scais"><img src="https://avatars.githubusercontent.com/u/4539192?v=4?s=100" width="100px;" alt=""/><br /><sub><b>scais</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ascais" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/sebbASF"><img src="https://avatars.githubusercontent.com/u/16689231?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sebbASF</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3AsebbASF" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/sergeygorbaty"><img src="https://avatars.githubusercontent.com/u/14813710?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sergeygorbaty</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=sergeygorbaty" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/shilko2013"><img src="https://avatars.githubusercontent.com/u/33313482?v=4?s=100" width="100px;" alt=""/><br /><sub><b>shilko2013</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ashilko2013" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/simeonKondr"><img src="https://avatars.githubusercontent.com/u/42644177?v=4?s=100" width="100px;" alt=""/><br /><sub><b>simeonKondr</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3AsimeonKondr" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/snajberk"><img src="https://avatars.githubusercontent.com/u/3585281?v=4?s=100" width="100px;" alt=""/><br /><sub><b>snajberk</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asnajberk" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/sniperrifle2004"><img src="https://avatars.githubusercontent.com/u/18223222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sniperrifle2004</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asniperrifle2004" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/sniperrifle2004"><img src="https://avatars.githubusercontent.com/u/18223222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sniperrifle2004</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asniperrifle2004" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/snuyanzin"><img src="https://avatars.githubusercontent.com/u/403174?v=4?s=100" width="100px;" alt=""/><br /><sub><b>snuyanzin</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asnuyanzin" title="Bug reports">🐛</a> <a href="https://github.com/pmd/pmd/commits?author=snuyanzin" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/sratz"><img src="https://avatars.githubusercontent.com/u/14908423?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sratz</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asratz" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/stonio"><img src="https://avatars.githubusercontent.com/u/19952825?v=4?s=100" width="100px;" alt=""/><br /><sub><b>stonio</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Astonio" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/sturton"><img src="https://avatars.githubusercontent.com/u/1734891?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sturton</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=sturton" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Asturton" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/sudharmohan"><img src="https://avatars.githubusercontent.com/u/16752281?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sudharmohan</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asudharmohan" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/suruchidawar"><img src="https://avatars.githubusercontent.com/u/30810931?v=4?s=100" width="100px;" alt=""/><br /><sub><b>suruchidawar</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asuruchidawar" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/svenfinitiv"><img src="https://avatars.githubusercontent.com/u/5653724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>svenfinitiv</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asvenfinitiv" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/svenfinitiv"><img src="https://avatars.githubusercontent.com/u/5653724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>svenfinitiv</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asvenfinitiv" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/tashiscool"><img src="https://avatars.githubusercontent.com/u/1057457?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tashiscool</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Atashiscool" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/test-git-hook"><img src="https://avatars.githubusercontent.com/u/49142715?v=4?s=100" width="100px;" alt=""/><br /><sub><b>test-git-hook</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Atest-git-hook" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/testation21"><img src="https://avatars.githubusercontent.com/u/47239708?v=4?s=100" width="100px;" alt=""/><br /><sub><b>testation21</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=testation21" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Atestation21" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/thanosa"><img src="https://avatars.githubusercontent.com/u/24596498?v=4?s=100" width="100px;" alt=""/><br /><sub><b>thanosa</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Athanosa" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/tiandiyixian"><img src="https://avatars.githubusercontent.com/u/27055337?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tiandiyixian</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Atiandiyixian" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/tobwoerk"><img src="https://avatars.githubusercontent.com/u/11739442?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tobwoerk</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Atobwoerk" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/tprouvot"><img src="https://avatars.githubusercontent.com/u/35368290?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tprouvot</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Atprouvot" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/tprouvot"><img src="https://avatars.githubusercontent.com/u/35368290?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tprouvot</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Atprouvot" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/trentchilders"><img src="https://avatars.githubusercontent.com/u/6664350?v=4?s=100" width="100px;" alt=""/><br /><sub><b>trentchilders</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Atrentchilders" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/triandicAnt"><img src="https://avatars.githubusercontent.com/u/2345902?v=4?s=100" width="100px;" alt=""/><br /><sub><b>triandicAnt</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3AtriandicAnt" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/trishul14"><img src="https://avatars.githubusercontent.com/u/24551131?v=4?s=100" width="100px;" alt=""/><br /><sub><b>trishul14</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Atrishul14" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/xmtsui"><img src="https://avatars.githubusercontent.com/u/1542690?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tsui</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Axmtsui" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/winhkey"><img src="https://avatars.githubusercontent.com/u/4877808?v=4?s=100" width="100px;" alt=""/><br /><sub><b>winhkey</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Awinhkey" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/witherspore"><img src="https://avatars.githubusercontent.com/u/813263?v=4?s=100" width="100px;" alt=""/><br /><sub><b>witherspore</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Awitherspore" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/wjljack"><img src="https://avatars.githubusercontent.com/u/1182478?v=4?s=100" width="100px;" alt=""/><br /><sub><b>wjljack</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Awjljack" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/wjljack"><img src="https://avatars.githubusercontent.com/u/1182478?v=4?s=100" width="100px;" alt=""/><br /><sub><b>wjljack</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Awjljack" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/wuchiuwong"><img src="https://avatars.githubusercontent.com/u/15967553?v=4?s=100" width="100px;" alt=""/><br /><sub><b>wuchiuwong</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Awuchiuwong" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/songxing10000"><img src="https://avatars.githubusercontent.com/u/10040131?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xingsong</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Asongxing10000" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/xioayuge"><img src="https://avatars.githubusercontent.com/u/45328272?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xioayuge</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Axioayuge" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/xnYi9wRezm"><img src="https://avatars.githubusercontent.com/u/61201892?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xnYi9wRezm</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=xnYi9wRezm" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3AxnYi9wRezm" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/xuanuy"><img src="https://avatars.githubusercontent.com/u/3894777?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xuanuy</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Axuanuy" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/xyf0921"><img src="https://avatars.githubusercontent.com/u/17350974?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xyf0921</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Axyf0921" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/yalechen-cyw3"><img src="https://avatars.githubusercontent.com/u/34886223?v=4?s=100" width="100px;" alt=""/><br /><sub><b>yalechen-cyw3</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ayalechen-cyw3" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/yalechen-cyw3"><img src="https://avatars.githubusercontent.com/u/34886223?v=4?s=100" width="100px;" alt=""/><br /><sub><b>yalechen-cyw3</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ayalechen-cyw3" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/yasuharu-sato"><img src="https://avatars.githubusercontent.com/u/45546628?v=4?s=100" width="100px;" alt=""/><br /><sub><b>yasuharu-sato</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ayasuharu-sato" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/zenglian"><img src="https://avatars.githubusercontent.com/u/5268434?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zenglian</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Azenglian" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/zgrzyt93"><img src="https://avatars.githubusercontent.com/u/54275965?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zgrzyt93</b></sub></a><br /><a href="https://github.com/pmd/pmd/commits?author=zgrzyt93" title="Code">💻</a> <a href="https://github.com/pmd/pmd/issues?q=author%3Azgrzyt93" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/zhangxinngang"><img src="https://avatars.githubusercontent.com/u/6891146?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zh3ng</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Azhangxinngang" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/yuchen1013"><img src="https://avatars.githubusercontent.com/u/17316917?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zt_soft</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Ayuchen1013" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/ztt79"><img src="https://avatars.githubusercontent.com/u/48408552?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ztt79</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aztt79" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/zzzzfeng"><img src="https://avatars.githubusercontent.com/u/8851007?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zzzzfeng</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Azzzzfeng" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/zzzzfeng"><img src="https://avatars.githubusercontent.com/u/8851007?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zzzzfeng</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Azzzzfeng" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/magwas"><img src="https://avatars.githubusercontent.com/u/756838?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Árpád Magosányi</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Amagwas" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/clsaa"><img src="https://avatars.githubusercontent.com/u/32028545?v=4?s=100" width="100px;" alt=""/><br /><sub><b>任贵杰</b></sub></a><br /><a href="https://github.com/pmd/pmd/issues?q=author%3Aclsaa" title="Bug reports">🐛</a></td>
</tr>

View File

@ -66,11 +66,11 @@ public class PmdExample {
public static void main(String[] args) {
PMDConfiguration configuration = new PMDConfiguration();
configuration.setInputPaths("/home/workspace/src/main/java/code");
configuration.setRuleSets("rulesets/java/quickstart.xml");
configuration.addRuleSet("rulesets/java/quickstart.xml");
configuration.setReportFormat("xml");
configuration.setReportFile("/home/workspace/pmd-report.xml");
PMD.runPMD(configuration);
PMD.runPmd(configuration);
}
}
```
@ -80,66 +80,75 @@ public class PmdExample {
This gives you more control over which files are processed, but is also more complicated.
You can also provide your own custom renderers.
1. First we create a `PMDConfiguration`. This is currently the only way to specify a ruleset:
1. First we create a `PMDConfiguration` and configure it, first the rules:
```java
PMDConfiguration configuration = new PMDConfiguration();
configuration.setMinimumPriority(RulePriority.MEDIUM);
configuration.setRuleSets("rulesets/java/quickstart.xml");
configuration.addRuleSet("rulesets/java/quickstart.xml");
```
2. In order to support type resolution, PMD needs to have access to the compiled classes and dependencies
as well. This is called "auxclasspath" and is also configured here.
2. Then we configure, which paths to analyze:
```java
configuration.setInputPaths("/home/workspace/src/main/java/code");
```
3. The we configure the default language version for Java. And in order to support type resolution,
PMD needs to have access to the compiled classes and dependencies as well. This is called
"auxclasspath" and is also configured here.
Note: you can specify multiple class paths separated by `:` on Unix-systems or `;` under Windows.
```java
configuration.prependClasspath("/home/workspace/target/classes:/home/.m2/repository/my/dependency.jar");
configuration.setDefaultLanguageVersion(LanguageRegistry.findLanguageByTerseName("java").getVersion("11"));
configuration.prependAuxClasspath("/home/workspace/target/classes:/home/.m2/repository/my/dependency.jar");
```
3. Then we need to load the rulesets. This is done by using the configuration, taking the minimum priority into
account:
4. Then we configure the reporting. Configuring the report file is optional. If not specified, the report
will be written to `stdout`.
```java
RuleSetLoader ruleSetLoader = RuleSetLoader.fromPmdConfig(configuration);
List<RuleSet> ruleSets = ruleSetLoader.loadFromResources(Arrays.asList(configuration.getRuleSets().split(",")));
configuration.setReportFormat("xml");
configuration.setReportFile("/home/workspace/pmd-report.xml");
```
4. PMD operates on a list of `DataSource`. You can assemble a own list of `FileDataSource`, e.g.
5. Now an optional step: If you want to use additional renderers as in the example, set them up before
calling PMD. You can use a built-in renderer, e.g. `XMLRenderer` or a custom renderer implementing
`Renderer`. Note, that you must manually initialize the renderer by setting a suitable `Writer`:
```java
List<DataSource> files = Arrays.asList(new FileDataSource(new File("/path/to/src/MyClass.java")));
```
5. For reporting, you can use `GlobalAnalysisListener`, which receives events like violations and errors.
Useful implementations are provided by `Renderer` instances. To use a renderer, eg the built-in `XMLRenderer`,
create it and configure it with a suitable `Writer`.
Writer rendererOutput = new StringWriter();
Renderer renderer = createRenderer(rendererOutput);
```java
StringWriter rendererOutput = new StringWriter();
Renderer xmlRenderer = new XMLRenderer("UTF-8");
xmlRenderer.setWriter(rendererOutput);
// The listener is created from the renderer in the next listing
// ...
private static Renderer createRenderer(Writer writer) {
XMLRenderer xml = new XMLRenderer("UTF-8");
xml.setWriter(writer);
return xml;
}
```
6. Now, all the preparations are done, and PMD can be executed. This is done by calling
`PMD.processFiles(...)`. This method call takes the configuration, the rulesets, the files
to process, and the list of renderers. Provide an empty list, if you don't want to use
any renderer. Note: The auxclasspath needs to be closed explicitly. Otherwise the class or jar files may
remain open and file resources are leaked.
6. Finally we can start the PMD analysis. There is the possibility to fine-tune the configuration
by adding additional files to analyze or adding additional rulesets or renderers:
```java
try (GlobalAnalysisListener listener = xmlRenderer.newListener()) {
PMD.processFiles(configuration, ruleSets, files, listener);
} finally {
ClassLoader auxiliaryClassLoader = configuration.getClassLoader();
if (auxiliaryClassLoader instanceof ClasspathClassLoader) {
((ClasspathClassLoader) auxiliaryClassLoader).close();
}
try (PmdAnalysis pmd = PmdAnalysis.create(configuration)) {
// optional: add more rulesets
pmd.addRuleSet(pmd.newRuleSetLoader().loadFromResource("custom-ruleset.xml"));
// optional: add more files
pmd.files().addFile(Paths.get("src", "main", "more-java", "ExtraSource.java"));
// optional: add more renderers
pmd.addRenderer(renderer);
// or just call PMD
pmd.performAnalysis();
}
```
7. After the call, the renderer will have been flushed by PMD (through its `GlobalAnalysisListener`).
Then you can check the rendered output.
The renderer will be automatically flushed and closed at the end of the analysis.
7. Then you can check the rendered output.
``` java
System.out.println("Rendered Report:");
@ -152,51 +161,43 @@ Here is a complete example:
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.nio.file.Paths;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.PmdAnalysis;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSetLoader;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.XMLRenderer;
import net.sourceforge.pmd.util.ClasspathClassLoader;
import net.sourceforge.pmd.util.datasource.DataSource;
import net.sourceforge.pmd.util.datasource.FileDataSource;
public class PmdExample2 {
public static void main(String[] args) throws IOException {
PMDConfiguration configuration = new PMDConfiguration();
configuration.setMinimumPriority(RulePriority.MEDIUM);
configuration.setRuleSets("rulesets/java/quickstart.xml");
configuration.prependClasspath("/home/workspace/target/classes");
RuleSetLoader ruleSetLoader = RuleSetLoader.fromPmdConfig(configuration);
List<RuleSet> ruleSets = ruleSetLoader.loadFromResources(Arrays.asList(configuration.getRuleSets().split(",")));
configuration.addRuleSet("rulesets/java/quickstart.xml");
List<DataSource> files = determineFiles("/home/workspace/src/main/java/code");
configuration.setInputPaths("/home/workspace/src/main/java/code");
configuration.setDefaultLanguageVersion(LanguageRegistry.findLanguageByTerseName("java").getVersion("11"));
configuration.prependAuxClasspath("/home/workspace/target/classes");
configuration.setReportFormat("xml");
configuration.setReportFile("/home/workspace/pmd-report.xml");
Writer rendererOutput = new StringWriter();
Renderer renderer = createRenderer(rendererOutput);
try (GlobalAnalysisListener listener = renderer.newListener()) {
PMD.processFiles(configuration, ruleSets, files, listener);
} finally {
ClassLoader auxiliaryClassLoader = configuration.getClassLoader();
if (auxiliaryClassLoader instanceof ClasspathClassLoader) {
((ClasspathClassLoader) auxiliaryClassLoader).close();
}
try (PmdAnalysis pmd = PmdAnalysis.create(configuration)) {
// optional: add more rulesets
pmd.addRuleSet(pmd.newRuleSetLoader().loadFromResource("custom-ruleset.xml"));
// optional: add more files
pmd.files().addFile(Paths.get("src", "main", "more-java", "ExtraSource.java"));
// optional: add more renderers
pmd.addRenderer(renderer);
// or just call PMD
pmd.performAnalysis();
}
System.out.println("Rendered Report:");
@ -208,28 +209,6 @@ public class PmdExample2 {
xml.setWriter(writer);
return xml;
}
private static List<DataSource> determineFiles(String basePath) throws IOException {
Path dirPath = FileSystems.getDefault().getPath(basePath);
final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
final List<DataSource> files = new ArrayList<>();
Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
if (matcher.matches(path.getFileName())) {
System.out.printf("Using %s%n", path);
files.add(new FileDataSource(path.toFile()));
} else {
System.out.printf("Ignoring %s%n", path);
}
return super.visitFile(path, attrs);
}
});
System.out.printf("Analyzing %d files in %s%n", files.size(), basePath);
return files;
}
}
```

View File

@ -19,11 +19,83 @@ This is a {{ site.pmd.release_type }} release.
### New and noteworthy
#### New programmatic API
This release introduces a new programmatic API to replace the inflexible {% jdoc core::PMD %} class.
Programmatic execution of PMD should now be done with a {% jdoc core::PMDConfiguration %}
and a {% jdoc core::PmdAnalysis %}, for instance:
```java
PMDConfiguration config = new PMDConfiguration();
config.setDefaultLanguageVersion(LanguageRegistry.findLanguageByTerseName("java").getVersion("11"));
config.setInputPaths("src/main/java");
config.prependAuxClasspath("target/classes");
config.setMinimumPriority(RulePriority.HIGH);
config.addRuleSet("rulesets/java/quickstart.xml");
config.setReportFormat("xml");
config.setReportFile("target/pmd-report.xml");
try (PmdAnalysis pmd = PmdAnalysis.create(config)) {
// note: don't use `config` once a PmdAnalysis has been created.
// optional: add more rulesets
pmd.addRuleSet(pmd.newRuleSetLoader().loadFromResource("custom-ruleset.xml"));
// optional: add more files
pmd.files().addFile(Paths.get("src", "main", "more-java", "ExtraSource.java"));
// optional: add more renderers
pmd.addRenderer(renderer);
// or just call PMD
pmd.performAnalysis();
}
```
The `PMD` class still supports methods related to CLI execution: `runPmd` and `main`.
All other members are now deprecated for removal.
The CLI itself remains compatible, if you run PMD via command-line, no action is required on your part.
### Fixed Issues
* apex-performance
* [#3773](https://github.com/pmd/pmd/pull/3773): \[apex] EagerlyLoadedDescribeSObjectResult false positives with SObjectField.getDescribe()
* core
* [#3299](https://github.com/pmd/pmd/issues/3299): \[core] Deprecate system properties of PMDCommandLineInterface
### API Changes
#### Deprecated API
* Several members of {% jdoc core::PMD %} have been newly deprecated, including:
- `PMD#EOL`: use `System#lineSeparator()`
- `PMD#SUPPRESS_MARKER`: use {% jdoc core::PMDConfiguration#DEFAULT_SUPPRESS_MARKER %}
- `PMD#processFiles`: use the [new programmatic API](#new-programmatic-api)
- `PMD#getApplicableFiles`: is internal
* {% jdoc !!core::PMDConfiguration#prependClasspath(java.lang.String) %} is deprecated
in favour of {% jdoc core::PMDConfiguration#prependAuxClasspath(java.lang.String) %}.
* {% jdoc !!core::PMDConfiguration#setRuleSets(java.lang.String) %} and
{% jdoc core::PMDConfiguration#getRuleSets() %} are deprecated. Use instead
{% jdoc core::PMDConfiguration#setRuleSets(java.util.List) %},
{% jdoc core::PMDConfiguration#addRuleSet(java.lang.String) %},
and {% jdoc core::PMDConfiguration#getRuleSetPaths() %}.
* Several members of {% jdoc test::cli.BaseCLITest %} have been deprecated with replacements.
* Several members of {% jdoc core::cli.PMDCommandLineInterface %} have been explicitly deprecated.
The whole class however was deprecated long ago already with 6.30.0. It is internal API and should
not be used.
#### Experimental APIs
* Together with the [new programmatic API](#new-programmatic-api) the interface
{% jdoc core::lang.document.TextFile %} has been added as *experimental*. It intends
to replace {% jdoc core::util.datasource.DataSource %} and {% jdoc core::cpd.SourceCode %} in the long term.
This interface will change in PMD 7 to support read/write operations
and other things. You don't need to use it in PMD 6, as {% jdoc core::lang.document.FileCollector %}
decouples you from this. A file collector is available through {% jdoc !!core::PmdAnalysis#files() %}.
### External Contributions
* [#3773](https://github.com/pmd/pmd/pull/3773): \[apex] EagerlyLoadedDescribeSObjectResult false positives with SObjectField.getDescribe() - [@filiprafalowicz](https://github.com/filiprafalowicz)
{% endtocmaker %}

View File

@ -56,4 +56,10 @@ public final class ASTReferenceExpression extends AbstractApexNode<ReferenceExpr
public boolean isSafeNav() {
return node.isSafeNav();
}
public boolean isSObjectType() {
List<Identifier> identifiers = node.getNames();
return identifiers != null
&& identifiers.stream().anyMatch(id -> "sobjecttype".equalsIgnoreCase(id.getValue()));
}
}

View File

@ -146,23 +146,26 @@ public class Something {
<description>
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
When using `SObjectType.getDescribe()` or `Schema.describeSObjects()` without supplying a `SObjectDescribeOptions`,
implicitly it will be using `SObjectDescribeOptions.DEFAULT` and 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
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
Lazy loading `DescribeSObjectResult` on picklist fields is not always 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.
`SObjectDescribeOptions.FULL` in such a case. The same applies when you need the same `DescribeSObjectResult`
to be consistent across 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)
(unless "Use Improved Schema Caching" critical update is applied, `SObjectDescribeOptions.DEFAULT` does 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.
</description>
@ -173,8 +176,20 @@ Properties:
<property name="xpath">
<value>
<![CDATA[
//MethodCallExpression[(lower-case(@MethodName) = "getdescribe" or lower-case(@MethodName) = "describesobjects") and not(VariableExpression/ReferenceExpression[lower-case(@Image) = "sobjectdescribeoptions" ])] |
//ReferenceExpression[$noDefault = true() and lower-case(@Image) = "sobjectdescribeoptions" and parent::VariableExpression[lower-case(@Image) = "default"]]
//MethodCallExpression
[
lower-case(@MethodName) = "getdescribe" and ReferenceExpression[@SObjectType = true()]
or lower-case(@MethodName) = "describesobjects"
]
[not(VariableExpression/ReferenceExpression
[lower-case(@Image) = ("sobjectdescribeoptions", "fielddescribeoptions")]
)
]
|
//ReferenceExpression
[$noDefault = true()]
[lower-case(@Image) = "sobjectdescribeoptions"]
[parent::VariableExpression[lower-case(@Image) = "default"]]
]]>
</value>
</property>

View File

@ -9,7 +9,7 @@
| +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "no location", @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", @Location = "(5, 13, 198, 199)", @Name = "anIntegerField", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "anIntegerField", @Location = "(5, 27, 212, 226)", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SObjectType = "false", @SafeNav = "true"]
| | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "anObject", @Location = "(5, 17, 202, 210)", @Namespace = "", @RealLoc = "true"]
| | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "x", @Location = "(5, 13, 198, 199)", @Namespace = "", @RealLoc = "true"]
@ -18,9 +18,9 @@
| +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "no location", @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", @Location = "(8, 12, 365, 375)", @Name = "profileUrl", @Namespace = "", @RealLoc = "true"]
| +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "toExternalForm", @InputParametersSize = "0", @Location = "(8, 47, 400, 414)", @MethodName = "toExternalForm", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SObjectType = "false", @SafeNav = "true"]
| | +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "user.getProfileUrl", @InputParametersSize = "0", @Location = "(8, 30, 383, 396)", @MethodName = "getProfileUrl", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Image = "user", @Location = "(8, 25, 378, 382)", @Namespace = "", @RealLoc = "true", @ReferenceType = "METHOD", @SafeNav = "false"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Image = "user", @Location = "(8, 25, 378, 382)", @Namespace = "", @RealLoc = "true", @ReferenceType = "METHOD", @SObjectType = "false", @SafeNav = "false"]
| +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "profileUrl", @Location = "(8, 12, 365, 375)", @Namespace = "", @RealLoc = "true"]
| +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
+- Method[@ApexVersion = "54.0", @Arity = "1", @CanonicalName = "bar1", @Constructor = "false", @DefiningType = "Foo", @Image = "bar1", @Location = "(10, 17, 435, 439)", @Namespace = "", @RealLoc = "true", @ReturnType = "void", @Synthetic = "false"]
@ -30,15 +30,15 @@
| +- BlockStatement[@ApexVersion = "54.0", @CurlyBrace = "true", @DefiningType = "Foo", @Location = "(10, 32, 450, 538)", @Namespace = "", @RealLoc = "true"]
| +- ExpressionStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Location = "(11, 12, 463, 465)", @Namespace = "", @RealLoc = "true"]
| | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "b", @Location = "(11, 12, 463, 464)", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SObjectType = "false", @SafeNav = "true"]
| | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a", @Location = "(11, 9, 460, 461)", @Namespace = "", @RealLoc = "true"]
| | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| +- ExpressionStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Location = "(12, 22, 527, 532)", @Namespace = "", @RealLoc = "true"]
| +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "c1", @InputParametersSize = "0", @Location = "(12, 22, 527, 529)", @MethodName = "c1", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SObjectType = "false", @SafeNav = "true"]
| +- CastExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Location = "(12, 10, 515, 518)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "b1", @Location = "(12, 17, 522, 524)", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SObjectType = "false", @SafeNav = "true"]
| +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a1", @Location = "(12, 13, 518, 520)", @Namespace = "", @RealLoc = "true"]
| +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
+- Method[@ApexVersion = "54.0", @Arity = "2", @CanonicalName = "bar2", @Constructor = "false", @DefiningType = "Foo", @Image = "bar2", @Location = "(15, 17, 556, 560)", @Namespace = "", @RealLoc = "true", @ReturnType = "void", @Synthetic = "false"]
@ -50,9 +50,9 @@
| +- BlockStatement[@ApexVersion = "54.0", @CurlyBrace = "true", @DefiningType = "Foo", @Location = "(15, 41, 580, 688)", @Namespace = "", @RealLoc = "true"]
| +- ExpressionStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Location = "(16, 25, 606, 613)", @Namespace = "", @RealLoc = "true"]
| | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "aField", @Location = "(16, 25, 606, 612)", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "false"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SObjectType = "false", @SafeNav = "false"]
| | +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "aMethod", @InputParametersSize = "0", @Location = "(16, 15, 596, 603)", @MethodName = "aMethod", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"]
| | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SObjectType = "false", @SafeNav = "true"]
| | +- ArrayLoadExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Location = "(16, 9, 590, 591)", @Namespace = "", @RealLoc = "true"]
| | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a", @Location = "(16, 9, 590, 591)", @Namespace = "", @RealLoc = "true"]
| | | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
@ -60,9 +60,9 @@
| | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| +- ExpressionStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Location = "(17, 25, 675, 682)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "aField", @Location = "(17, 25, 675, 681)", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SObjectType = "false", @SafeNav = "true"]
| +- MethodCallExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @FullMethodName = "aMethod", @InputParametersSize = "0", @Location = "(17, 14, 664, 671)", @MethodName = "aMethod", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "false"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SObjectType = "false", @SafeNav = "false"]
| +- ArrayLoadExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Location = "(17, 9, 659, 660)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "a", @Location = "(17, 9, 659, 660)", @Namespace = "", @RealLoc = "true"]
| | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
@ -77,14 +77,14 @@
| | +- ModifierNode[@Abstract = "false", @ApexVersion = "54.0", @DefiningType = "Foo", @DeprecatedTestMethod = "false", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "no location", @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", @Location = "(21, 16, 744, 745)", @Namespace = "", @RealLoc = "true", @Type = "String"]
| | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "BillingCity", @Location = "(21, 37, 765, 776)", @Namespace = "", @RealLoc = "true"]
| | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SObjectType = "false", @SafeNav = "true"]
| | | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "Account", @Location = "(21, 28, 756, 763)", @Namespace = "", @RealLoc = "true"]
| | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Image = "contact", @Location = "(21, 20, 748, 755)", @Namespace = "", @RealLoc = "true", @ReferenceType = "LOAD", @SafeNav = "false"]
| | | +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Image = "contact", @Location = "(21, 20, 748, 755)", @Namespace = "", @RealLoc = "true", @ReferenceType = "LOAD", @SObjectType = "false", @SafeNav = "false"]
| | +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "s", @Location = "(21, 16, 744, 745)", @Namespace = "", @RealLoc = "true"]
| | +- EmptyReferenceExpression[@ApexVersion = "54.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| +- ReturnStatement[@ApexVersion = "54.0", @DefiningType = "Foo", @Location = "(23, 9, 841, 899)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "Name", @Location = "(23, 62, 894, 898)", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| +- ReferenceExpression[@ApexVersion = "54.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SObjectType = "false", @SafeNav = "true"]
| +- SoqlExpression[@ApexVersion = "54.0", @CanonicalQuery = "SELECT Name FROM Account WHERE Id = :tmpVar1", @DefiningType = "Foo", @Location = "(23, 16, 848, 892)", @Namespace = "", @Query = "SELECT Name FROM Account WHERE Id = :accId", @RealLoc = "true"]
| +- BindExpressions[@ApexVersion = "54.0", @DefiningType = "Foo", @Location = "(23, 16, 848, 892)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "54.0", @DefiningType = "Foo", @Image = "accId", @Location = "(23, 54, 886, 891)", @Namespace = "", @RealLoc = "true"]

View File

@ -7,6 +7,7 @@
<test-code>
<description>No describer options</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<code><![CDATA[
public class Foo {
public void bar(List<Account> accounts) {
@ -21,6 +22,7 @@ public class Foo {
<test-code>
<description>No describer options using Schema class</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<code><![CDATA[
public class Foo {
public void bar(List<Account> accounts) {
@ -89,4 +91,54 @@ public class Foo {
]]></code>
</test-code>
<test-code>
<description>False positive with no describer options on SObjectField</description>
<expected-problems>0</expected-problems>
<!-- note, this is not a violation. The default behaviour for SObjectField.getDescribe()
doesn't seem to be a performance problem. #3773 -->
<code><![CDATA[
public class Foo {
public void bar(Case case) {
String fieldName = Case.Subject.getDescribe().getName();
}
}
]]></code>
</test-code>
<test-code>
<description>False positive on SObjectField with FieldDescribeOptions.FULL_DESCRIBE</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
public void bar(Case case) {
String fieldName = Case.Subject.getDescribe(FieldDescribeOptions.FULL_DESCRIBE).getName();
}
}
]]></code>
</test-code>
<test-code>
<description>False positive on SObjectField with FieldDescribeOptions.DEFAULT</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
public void bar(Case case) {
String fieldName = Case.Subject.getDescribe(FieldDescribeOptions.DEFAULT).getName();
}
}
]]></code>
</test-code>
<test-code>
<description>False positive on SObjectField with FieldDescribeOptions.DEFAULT with noDefault=true</description>
<rule-property name="noDefault">true</rule-property>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
public void bar(Case case) {
String fieldName = Case.Subject.getDescribe(FieldDescribeOptions.DEFAULT).getName();
}
}
]]></code>
</test-code>
</test-data>

File diff suppressed because it is too large Load Diff

View File

@ -13,12 +13,14 @@ import java.util.Objects;
import java.util.Properties;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.annotation.DeprecatedUntil700;
import net.sourceforge.pmd.cache.AnalysisCache;
import net.sourceforge.pmd.cache.FileAnalysisCache;
import net.sourceforge.pmd.cache.NoopAnalysisCache;
import net.sourceforge.pmd.cli.PmdParametersParseResult;
import net.sourceforge.pmd.internal.util.AssertionUtil;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
@ -44,7 +46,7 @@ import net.sourceforge.pmd.util.ClasspathClassLoader;
* {@link #getClassLoader()}</li>
* <li>A means to configure a ClassLoader using a prepended classpath String,
* instead of directly setting it programmatically.
* {@link #prependClasspath(String)}</li>
* {@link #prependAuxClasspath(String)}</li>
* <li>A LanguageVersionDiscoverer instance, which defaults to using the default
* LanguageVersion of each Language. Means are provided to change the
* LanguageVersion for each Language.
@ -88,15 +90,19 @@ import net.sourceforge.pmd.util.ClasspathClassLoader;
* </ul>
*/
public class PMDConfiguration extends AbstractConfiguration {
/** The default suppress marker string. */
public static final String DEFAULT_SUPPRESS_MARKER = "NOPMD";
// General behavior options
private String suppressMarker = PMD.SUPPRESS_MARKER;
private String suppressMarker = DEFAULT_SUPPRESS_MARKER;
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<String> ruleSets;
private List<String> ruleSets = new ArrayList<>();
private RulePriority minimumPriority = RulePriority.LOW;
private String inputPaths;
private String inputUri;
@ -197,13 +203,46 @@ public class PMDConfiguration extends AbstractConfiguration {
* if the given classpath is invalid (e.g. does not exist)
* @see PMDConfiguration#setClassLoader(ClassLoader)
* @see ClasspathClassLoader
*
* @deprecated Use {@link #prependAuxClasspath(String)}, which doesn't
* throw a checked {@link IOException}
*/
@Deprecated
public void prependClasspath(String classpath) throws IOException {
if (classLoader == null) {
classLoader = PMDConfiguration.class.getClassLoader();
try {
prependAuxClasspath(classpath);
} catch (IllegalArgumentException e) {
throw new IOException(e);
}
if (classpath != null) {
classLoader = new ClasspathClassLoader(classpath, classLoader);
}
/**
* Prepend the specified classpath like string to the current ClassLoader of
* the configuration. If no ClassLoader is currently configured, the
* ClassLoader used to load the {@link PMDConfiguration} class will be used
* as the parent ClassLoader of the created ClassLoader.
*
* <p>If the classpath String looks like a URL to a file (i.e. starts with
* <code>file://</code>) the file will be read with each line representing
* an entry on the classpath.</p>
*
* @param classpath The prepended classpath.
*
* @throws IllegalArgumentException if the given classpath is invalid (e.g. does not exist)
* @see PMDConfiguration#setClassLoader(ClassLoader)
*/
public void prependAuxClasspath(String classpath) {
try {
if (classLoader == null) {
classLoader = PMDConfiguration.class.getClassLoader();
}
if (classpath != null) {
classLoader = new ClasspathClassLoader(classpath, classLoader);
}
} catch (IOException e) {
// Note: IOExceptions shouldn't appear anymore, they should already be converted
// to IllegalArgumentException in ClasspathClassLoader.
throw new IllegalArgumentException(e);
}
}
@ -244,6 +283,7 @@ public class PMDConfiguration extends AbstractConfiguration {
*/
public void setForceLanguageVersion(LanguageVersion forceLanguageVersion) {
this.forceLanguageVersion = forceLanguageVersion;
languageVersionDiscoverer.setForcedVersion(forceLanguageVersion);
}
/**
@ -253,6 +293,7 @@ public class PMDConfiguration extends AbstractConfiguration {
* the LanguageVersion
*/
public void setDefaultLanguageVersion(LanguageVersion languageVersion) {
Objects.requireNonNull(languageVersion);
setDefaultLanguageVersions(Arrays.asList(languageVersion));
}
@ -265,6 +306,7 @@ public class PMDConfiguration extends AbstractConfiguration {
*/
public void setDefaultLanguageVersions(List<LanguageVersion> languageVersions) {
for (LanguageVersion languageVersion : languageVersions) {
Objects.requireNonNull(languageVersion);
languageVersionDiscoverer.setDefaultLanguageVersion(languageVersion);
}
}
@ -310,7 +352,10 @@ public class PMDConfiguration extends AbstractConfiguration {
*/
@Deprecated
@DeprecatedUntil700
public String getRuleSets() {
public @Nullable String getRuleSets() {
if (ruleSets.isEmpty()) {
return null;
}
return String.join(",", ruleSets);
}
@ -319,17 +364,34 @@ public class PMDConfiguration extends AbstractConfiguration {
*
* @see RuleSetLoader#loadFromResource(String)
*/
public List<String> getRuleSetPaths() {
public @NonNull List<@NonNull String> getRuleSetPaths() {
return ruleSets;
}
/**
* Sets the rulesets.
* Sets the list of ruleset paths to load when starting the analysis.
*
* @param ruleSetPaths A list of ruleset paths, understandable by {@link RuleSetLoader#loadFromResource(String)}.
*
* @throws NullPointerException If the parameter is null
*/
public void setRuleSets(@NonNull List<String> ruleSets) {
this.ruleSets = new ArrayList<>(ruleSets);
public void setRuleSets(@NonNull List<@NonNull String> ruleSetPaths) {
AssertionUtil.requireParamNotNull("ruleSetPaths", ruleSetPaths);
AssertionUtil.requireContainsNoNullValue("ruleSetPaths", ruleSetPaths);
this.ruleSets = new ArrayList<>(ruleSetPaths);
}
/**
* Add a new ruleset paths to load when starting the analysis.
* This list is initially empty.
*
* @param rulesetPath A ruleset path, understandable by {@link RuleSetLoader#loadFromResource(String)}.
*
* @throws NullPointerException If the parameter is null
*/
public void addRuleSet(@NonNull String rulesetPath) {
AssertionUtil.requireParamNotNull("rulesetPath", rulesetPath);
this.ruleSets.add(rulesetPath);
}
/**
@ -337,12 +399,16 @@ public class PMDConfiguration extends AbstractConfiguration {
*
* @param ruleSets the rulesets to set
*
* @deprecated Use {@link #setRuleSets(List)}
* @deprecated Use {@link #setRuleSets(List)} or {@link #addRuleSet(String)}.
*/
@Deprecated
@DeprecatedUntil700
public void setRuleSets(String ruleSets) {
this.ruleSets = Arrays.asList(ruleSets.split(","));
public void setRuleSets(@Nullable String ruleSets) {
if (ruleSets == null) {
this.ruleSets = new ArrayList<>();
} else {
this.ruleSets = new ArrayList<>(Arrays.asList(ruleSets.split(",")));
}
}
/**

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@ import net.sourceforge.pmd.util.datasource.DataSource;
* and configuration errors.
*
* <p>A report may be created by a {@link GlobalReportBuilderListener} that you
* use as the {@link GlobalAnalysisListener} in {@linkplain PMD#processFiles(PMDConfiguration, List, List, GlobalAnalysisListener) PMD's entry point}.
* use as the {@linkplain GlobalAnalysisListener} in {@link PmdAnalysis#performAnalysisAndCollectReport() PMD's entry point}.
* You can also create one manually with {@link #buildReport(Consumer)}.
*/
public final class Report {

View File

@ -16,11 +16,16 @@ import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.util.CollectionUtil;
import net.sourceforge.pmd.util.ResourceLoader;
import net.sourceforge.pmd.util.log.MessageReporter;
import net.sourceforge.pmd.util.log.internal.NoopReporter;
/**
* Configurable object to load rulesets from XML resources.
@ -29,12 +34,26 @@ import net.sourceforge.pmd.util.ResourceLoader;
* or some such overload.
*/
public final class RuleSetLoader {
private static final Logger LOG = LoggerFactory.getLogger(RuleSetLoader.class);
private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader());
private RulePriority minimumPriority = RulePriority.LOW;
private boolean warnDeprecated = true;
private @NonNull RuleSetFactoryCompatibility compatFilter = RuleSetFactoryCompatibility.DEFAULT;
private boolean includeDeprecatedRuleReferences = false;
private MessageReporter reporter = new NoopReporter(); // non-null
/**
* Create a new RuleSetLoader with a default configuration.
* The defaults are described on each configuration method of this class.
*/
public RuleSetLoader() { // NOPMD UnnecessaryConstructor
// default
}
void setReporter(MessageReporter reporter) {
this.reporter = reporter;
}
/**
* Specify that the given classloader should be used to resolve
@ -145,7 +164,7 @@ public final class RuleSetLoader {
*
* @throws RuleSetLoadException If any error occurs (eg, invalid syntax)
*/
public RuleSet loadFromString(String filename, String rulesetXmlContent) {
public RuleSet loadFromString(String filename, final String rulesetXmlContent) {
return loadFromResource(new RuleSetReferenceId(filename) {
@Override
public InputStream getInputStream(ResourceLoader rl) {
@ -171,6 +190,52 @@ public final class RuleSetLoader {
return ruleSets;
}
/**
* Loads a list of rulesets, if any has an error, report it on the contextual
* error reporter instead of aborting, and continue loading the rest.
*
* <p>Internal API: might be published later, or maybe in PMD 7 this
* will be the default behaviour of every method of this class.
*/
@InternalApi
public List<RuleSet> loadRuleSetsWithoutException(List<String> rulesetPaths) {
List<RuleSet> ruleSets = new ArrayList<>(rulesetPaths.size());
boolean anyRules = false;
for (String path : rulesetPaths) {
try {
RuleSet ruleset = this.loadFromResource(path);
anyRules |= !ruleset.getRules().isEmpty();
printRulesInDebug(path, ruleset);
ruleSets.add(ruleset);
} catch (RuleSetLoadException e) {
if (e.getCause() != null) {
// eg RuleSetNotFoundException
reporter.errorEx("Cannot load ruleset {0}", new Object[] { path }, e.getCause());
} else {
reporter.errorEx("Cannot load ruleset {0}", new Object[] { path }, e);
}
}
}
if (!anyRules) {
reporter.warn("No rules found. Maybe you misspelled a rule name? ({})",
StringUtils.join(rulesetPaths, ','));
}
return ruleSets;
}
void printRulesInDebug(String path, RuleSet ruleset) {
if (LOG.isDebugEnabled()) {
LOG.debug("Rules loaded from {}:", path);
for (Rule rule : ruleset.getRules()) {
LOG.debug("- {} ({})", rule.getName(), rule.getLanguage().getName());
}
}
if (ruleset.getRules().isEmpty()) {
reporter.warn("No rules found in ruleset {}", path);
}
}
/**
* Parses several resources into a list of rulesets.
*

View File

@ -4,10 +4,9 @@
package net.sourceforge.pmd.ant.internal;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;
@ -22,12 +21,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.PmdAnalysis;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSetLoadException;
import net.sourceforge.pmd.RuleSetLoader;
import net.sourceforge.pmd.ant.Formatter;
import net.sourceforge.pmd.ant.PMDTask;
@ -38,11 +34,11 @@ import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.reporting.FileAnalysisListener;
import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
import net.sourceforge.pmd.reporting.GlobalAnalysisListener.ViolationCounterListener;
import net.sourceforge.pmd.reporting.ReportStats;
import net.sourceforge.pmd.reporting.ReportStatsListener;
import net.sourceforge.pmd.util.ClasspathClassLoader;
import net.sourceforge.pmd.util.IOUtil;
import net.sourceforge.pmd.util.datasource.DataSource;
import net.sourceforge.pmd.util.datasource.FileDataSource;
public class PMDTaskImpl {
@ -51,7 +47,7 @@ public class PMDTaskImpl {
private final List<Formatter> formatters = new ArrayList<>();
private final List<FileSet> filesets = new ArrayList<>();
private final PMDConfiguration configuration = new PMDConfiguration();
private final String rulesetPaths;
private boolean failOnError;
private boolean failOnRuleViolation;
private int maxRuleViolations = 0;
private String failuresPropertyName;
@ -62,12 +58,16 @@ public class PMDTaskImpl {
if (task.getSuppressMarker() != null) {
configuration.setSuppressMarker(task.getSuppressMarker());
}
this.failOnError = task.isFailOnError();
this.failOnRuleViolation = task.isFailOnRuleViolation();
this.maxRuleViolations = task.getMaxRuleViolations();
if (this.maxRuleViolations > 0) {
this.failOnRuleViolation = true;
}
this.rulesetPaths = task.getRulesetFiles() == null ? "" : task.getRulesetFiles();
if (task.getRulesetFiles() != null) {
configuration.setRuleSets(Arrays.asList(task.getRulesetFiles().split(",")));
}
configuration.setRuleSetFactoryCompatibilityEnabled(!task.isNoRuleSetCompatibility());
if (task.getEncoding() != null) {
configuration.setSourceEncoding(task.getEncoding());
@ -100,46 +100,49 @@ public class PMDTaskImpl {
private void doTask() {
setupClassLoader();
// Setup RuleSetFactory and validate RuleSets
RuleSetLoader rulesetLoader = RuleSetLoader.fromPmdConfig(configuration)
.loadResourcesWith(setupResourceLoader());
List<RuleSet> rules = loadRulesets(rulesetLoader);
if (configuration.getSuppressMarker() != null) {
project.log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE);
}
@SuppressWarnings("PMD.CloseResource")
ViolationCounterListener reportSizeListener = new ViolationCounterListener();
final List<DataSource> files = new ArrayList<>();
final List<String> reportShortNamesPaths = new ArrayList<>();
@SuppressWarnings("PMD.CloseResource") final List<String> reportShortNamesPaths = new ArrayList<>();
StringJoiner fullInputPath = new StringJoiner(",");
for (FileSet fs : filesets) {
DirectoryScanner ds = fs.getDirectoryScanner(project);
for (String srcFile : ds.getIncludedFiles()) {
File file = new File(ds.getBasedir() + File.separator + srcFile);
files.add(new FileDataSource(file));
List<String> ruleSetPaths = expandRuleSetPaths(configuration.getRuleSetPaths());
// don't let PmdAnalysis.create create rulesets itself.
configuration.setRuleSets(Collections.emptyList());
ReportStats stats;
try (PmdAnalysis pmd = PmdAnalysis.create(configuration)) {
RuleSetLoader rulesetLoader =
pmd.newRuleSetLoader().loadResourcesWith(setupResourceLoader());
pmd.addRuleSets(rulesetLoader.loadRuleSetsWithoutException(ruleSetPaths));
for (FileSet fileset : filesets) {
DirectoryScanner ds = fileset.getDirectoryScanner(project);
for (String srcFile : ds.getIncludedFiles()) {
pmd.files().addFile(ds.getBasedir().toPath().resolve(srcFile));
}
final String commonInputPath = ds.getBasedir().getPath();
fullInputPath.add(commonInputPath);
if (configuration.isReportShortNames()) {
reportShortNamesPaths.add(commonInputPath);
}
}
final String commonInputPath = ds.getBasedir().getPath();
fullInputPath.add(commonInputPath);
if (configuration.isReportShortNames()) {
reportShortNamesPaths.add(commonInputPath);
@SuppressWarnings("PMD.CloseResource")
ReportStatsListener reportStatsListener = new ReportStatsListener();
pmd.addListener(getListener(reportStatsListener, reportShortNamesPaths, fullInputPath.toString()));
pmd.performAnalysis();
stats = reportStatsListener.getResult();
if (failOnError && pmd.getReporter().numErrors() > 0) {
throw new BuildException("Some errors occurred while running PMD");
}
}
configuration.setInputPaths(fullInputPath.toString());
try (GlobalAnalysisListener listener = getListener(reportSizeListener, reportShortNamesPaths)) {
PMD.processFiles(configuration, rules, files, listener);
} catch (Exception e) {
throw new BuildException("Exception while closing data sources", e);
}
int problemCount = reportSizeListener.getResult();
int problemCount = stats.getNumViolations();
project.log(problemCount + " problems found", Project.MSG_VERBOSE);
if (failuresPropertyName != null && problemCount > 0) {
@ -152,34 +155,27 @@ public class PMDTaskImpl {
}
}
private List<RuleSet> loadRulesets(RuleSetLoader rulesetLoader) {
try {
// This is just used to validate and display rules. Each thread will create its own ruleset
// Substitute env variables/properties
String ruleSetString = project.replaceProperties(rulesetPaths);
List<String> rulesets = Arrays.asList(ruleSetString.split(","));
List<RuleSet> rulesetList = rulesetLoader.loadFromResources(rulesets);
if (rulesetList.isEmpty()) {
throw new BuildException("No rulesets");
}
logRulesUsed(rulesetList);
return rulesetList;
} catch (RuleSetLoadException e) {
throw new BuildException(e.getMessage(), e);
private List<String> expandRuleSetPaths(List<String> ruleSetPaths) {
List<String> paths = new ArrayList<>(ruleSetPaths);
for (int i = 0; i < paths.size(); i++) {
paths.set(i, project.replaceProperties(paths.get(i)));
}
return paths;
}
private @NonNull GlobalAnalysisListener getListener(ViolationCounterListener reportSizeListener, List<String> reportShortNamesPaths) {
private @NonNull GlobalAnalysisListener getListener(ReportStatsListener reportSizeListener,
List<String> reportShortNamesPaths,
String inputPaths) {
List<GlobalAnalysisListener> renderers = new ArrayList<>(formatters.size() + 1);
try {
renderers.add(makeLogListener(configuration.getInputPaths()));
renderers.add(makeLogListener(inputPaths));
renderers.add(reportSizeListener);
for (Formatter formatter : formatters) {
project.log("Sending a report to " + formatter, Project.MSG_VERBOSE);
renderers.add(formatter.newListener(project, reportShortNamesPaths));
}
} catch (IOException e) {
return GlobalAnalysisListener.tee(renderers);
} catch (Exception e) {
// close those opened so far
Exception e2 = IOUtil.closeAll(renderers);
if (e2 != null) {
@ -187,8 +183,6 @@ public class PMDTaskImpl {
}
throw new BuildException("Exception while initializing renderers", e);
}
return GlobalAnalysisListener.tee(renderers);
}
private GlobalAnalysisListener makeLogListener(String commonInputPath) {
@ -233,9 +227,9 @@ public class PMDTaskImpl {
try {
if (auxClasspath != null) {
project.log("Using auxclasspath: " + auxClasspath, Project.MSG_VERBOSE);
configuration.prependClasspath(auxClasspath.toString());
configuration.prependAuxClasspath(auxClasspath.toString());
}
} catch (IOException ioe) {
} catch (IllegalArgumentException ioe) {
throw new BuildException(ioe.getMessage(), ioe);
}
}
@ -257,13 +251,4 @@ public class PMDTaskImpl {
}
}
private void logRulesUsed(List<RuleSet> rulesets) {
project.log("Using these rulesets: " + rulesetPaths, Project.MSG_VERBOSE);
for (RuleSet ruleSet : rulesets) {
for (Rule rule : ruleSet.getRules()) {
project.log("Using rule " + rule.getName(), Project.MSG_VERBOSE);
}
}
}
}

View File

@ -26,6 +26,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.PMDVersion;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.annotation.InternalApi;
@ -210,13 +211,26 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
}
@Override
public FileAnalysisListener startFileAnalysis(DataSource filename) {
return violation -> {
final AnalysisResult analysisResult =
updatedResultsCache.get(violation.getFilename());
public FileAnalysisListener startFileAnalysis(DataSource dataSource) {
String fileName = dataSource.getNiceFileName(false, "");
File sourceFile = new File(fileName);
AnalysisResult analysisResult = updatedResultsCache.get(fileName);
if (analysisResult == null) {
analysisResult = new AnalysisResult(sourceFile);
}
final AnalysisResult nonNullAnalysisResult = analysisResult;
synchronized (analysisResult) {
analysisResult.addViolation(violation);
return new FileAnalysisListener() {
@Override
public void onRuleViolation(RuleViolation violation) {
synchronized (nonNullAnalysisResult) {
nonNullAnalysisResult.addViolation(violation);
}
}
@Override
public void onError(ProcessingError error) {
analysisFailed(sourceFile);
}
};
}

View File

@ -5,6 +5,7 @@
package net.sourceforge.pmd.cache;
import java.io.File;
import java.io.IOException;
import java.util.List;
import net.sourceforge.pmd.RuleSets;
@ -12,6 +13,7 @@ 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.datasource.DataSource;
/**
* An analysis cache for incremental analysis.
@ -22,12 +24,12 @@ import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
*/
@Deprecated
@InternalApi
public interface AnalysisCache extends GlobalAnalysisListener {
public interface AnalysisCache {
/**
* Persists the updated analysis results on whatever medium is used by the cache.
*/
void persist();
void persist() throws IOException;
/**
* Checks if a given file is up to date in the cache and can be skipped from analysis.
@ -59,8 +61,16 @@ public interface AnalysisCache extends GlobalAnalysisListener {
* cache is invalidated. This needs to be called before analysis, as it
* conditions the good behaviour of {@link #isUpToDate(File)}.
*
* @param ruleSets The rulesets configured for this analysis.
* @param ruleSets The rulesets configured for this analysis.
* @param auxclassPathClassLoader The class loader for auxclasspath configured for this analysis.
*/
void checkValidity(RuleSets ruleSets, ClassLoader auxclassPathClassLoader);
/**
* Returns a listener that will be used like in {@link GlobalAnalysisListener#startFileAnalysis(DataSource)}.
* This should record violations, and call {@link #analysisFailed(File)}
* upon error.
*/
FileAnalysisListener startFileAnalysis(DataSource file);
}

View File

@ -0,0 +1,40 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cache;
import java.io.IOException;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.reporting.FileAnalysisListener;
import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
import net.sourceforge.pmd.util.datasource.DataSource;
/**
* Adapter to wrap {@link AnalysisCache} behaviour in a {@link GlobalAnalysisListener}.
*/
@Deprecated
@InternalApi
public class AnalysisCacheListener implements GlobalAnalysisListener {
private final AnalysisCache cache;
public AnalysisCacheListener(AnalysisCache cache, RuleSets ruleSets, ClassLoader classLoader) {
this.cache = cache;
cache.checkValidity(ruleSets, classLoader);
}
@Override
public FileAnalysisListener startFileAnalysis(DataSource file) {
return cache.startFileAnalysis(file);
}
@Override
public void close() throws IOException {
cache.persist();
}
}

View File

@ -151,11 +151,6 @@ public class FileAnalysisCache extends AbstractAnalysisCache {
}
}
@Override
public void close() throws Exception {
// nothing to do, PMD calls persist explicitly
}
@Override
protected boolean cacheExists() {
return cacheFile.exists() && cacheFile.isFile() && cacheFile.length() > 0;

View File

@ -53,8 +53,4 @@ public class NoopAnalysisCache implements AnalysisCache {
return FileAnalysisListener.noop();
}
@Override
public void close() throws Exception {
// noop
}
}

View File

@ -8,6 +8,7 @@ import java.util.Properties;
import java.util.stream.Collectors;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMD.StatusCode;
import net.sourceforge.pmd.PMDVersion;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.Language;
@ -28,13 +29,39 @@ import com.beust.jcommander.ParameterException;
@InternalApi
public final class PMDCommandLineInterface {
@Deprecated
public static final String PROG_NAME = "pmd";
/**
* @deprecated This is used for testing, but support for it will be removed in PMD 7.
* Use {@link PMD#runPmd(String...)} or an overload to avoid exiting the VM. In PMD 7,
* {@link PMD#main(String[])} will call {@link System#exit(int)} always.
*/
@Deprecated
public static final String NO_EXIT_AFTER_RUN = "net.sourceforge.pmd.cli.noExit";
/**
* @deprecated This is used for testing, but support for it will be removed in PMD 7.
* Use {@link PMD#runPmd(String...)} or an overload to avoid exiting the VM. In PMD 7,
* {@link PMD#main(String[])} will call {@link System#exit(int)} always.
*/
@Deprecated
public static final String STATUS_CODE_PROPERTY = "net.sourceforge.pmd.cli.status";
/**
* @deprecated Use {@link StatusCode#OK}
*/
@Deprecated
public static final int NO_ERRORS_STATUS = 0;
/**
* @deprecated Use {@link StatusCode#ERROR}
*/
@Deprecated
public static final int ERROR_STATUS = 1;
/**
* @deprecated Use {@link StatusCode#VIOLATIONS_FOUND}
*/
@Deprecated
public static final int VIOLATIONS_FOUND = 4;
private PMDCommandLineInterface() { }
@ -126,7 +153,10 @@ public final class PMDCommandLineInterface {
* For testing purpose only...
*
* @param args
*
* @deprecated Use {@link PMD#runPmd(String...)}
*/
@Deprecated
public static void main(String[] args) {
System.out.println(PMDCommandLineInterface.buildUsageText());
}
@ -160,14 +190,6 @@ public final class PMDCommandLineInterface {
return buf.toString();
}
/**
* @deprecated Use {@link PMD#main(String[])}
*/
@Deprecated
public static void run(String[] args) {
setStatusCodeOrExit(PMD.run(args));
}
public static void setStatusCodeOrExit(int status) {
if (isExitAfterRunSet()) {
System.exit(status);

View File

@ -4,7 +4,6 @@
package net.sourceforge.pmd.cli;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -235,8 +234,8 @@ public class PMDParameters {
}
try {
configuration.prependClasspath(this.getAuxclasspath());
} catch (IOException e) {
configuration.prependAuxClasspath(this.getAuxclasspath());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid auxiliary classpath: " + e.getMessage(), e);
}
return configuration;

View File

@ -25,10 +25,12 @@ public final class AssertionUtil {
/** @throws NullPointerException if $name */
public static void requireContainsNoNullValue(String name, Collection<?> c) {
int i = 0;
for (Object o : c) {
if (o == null) {
throw new NullPointerException(name + " contains null elements");
throw new NullPointerException(name + " contains a null element at index " + i);
}
i++;
}
}

View File

@ -0,0 +1,183 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.internal.util;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.document.FileCollector;
import net.sourceforge.pmd.lang.document.TextFile;
import net.sourceforge.pmd.util.FileUtil;
import net.sourceforge.pmd.util.database.DBMSMetadata;
import net.sourceforge.pmd.util.database.DBURI;
import net.sourceforge.pmd.util.database.SourceObject;
import net.sourceforge.pmd.util.datasource.DataSource;
import net.sourceforge.pmd.util.log.MessageReporter;
import net.sourceforge.pmd.util.log.internal.ErrorsAsWarningsReporter;
/**
* @author Clément Fournier
*/
public final class FileCollectionUtil {
private static final Logger LOG = LoggerFactory.getLogger(FileCollectionUtil.class);
private FileCollectionUtil() {
}
public static List<DataSource> collectorToDataSource(FileCollector collector) {
List<DataSource> result = new ArrayList<>();
for (TextFile file : collector.getCollectedFiles()) {
result.add(file.toDataSourceCompat());
}
return result;
}
public static FileCollector collectFiles(PMDConfiguration configuration, Set<Language> languages, MessageReporter reporter) {
FileCollector collector = collectFiles(configuration, reporter);
collector.filterLanguages(languages);
return collector;
}
private static FileCollector collectFiles(PMDConfiguration configuration, MessageReporter reporter) {
FileCollector collector = FileCollector.newCollector(
configuration.getLanguageVersionDiscoverer(),
reporter
);
collectFiles(configuration, collector);
return collector;
}
public static void collectFiles(PMDConfiguration configuration, FileCollector collector) {
if (configuration.getSourceEncoding() != null) {
collector.setCharset(configuration.getSourceEncoding());
}
if (configuration.getInputPaths() != null) {
collectFiles(collector, configuration.getInputPaths());
}
if (configuration.getInputUri() != null) {
collectDB(collector, configuration.getInputUri());
}
if (configuration.getInputFilePath() != null) {
collectFileList(collector, configuration.getInputFilePath());
}
if (configuration.getIgnoreFilePath() != null) {
// This is to be able to interpret the log (will report 'adding' xxx)
LOG.debug("Now collecting files to exclude.");
// errors like "excluded file does not exist" are reported as warnings.
// todo better reporting of *where* exactly the path is
MessageReporter mutedLog = new ErrorsAsWarningsReporter(collector.getReporter());
try (FileCollector excludeCollector = collector.newCollector(mutedLog)) {
collectFileList(excludeCollector, configuration.getIgnoreFilePath());
collector.exclude(excludeCollector);
}
}
}
public static void collectFiles(FileCollector collector, String fileLocations) {
for (String rootLocation : fileLocations.split(",")) {
try {
collector.relativizeWith(rootLocation);
addRoot(collector, rootLocation);
} catch (IOException e) {
collector.getReporter().errorEx("Error collecting " + rootLocation, e);
}
}
}
public static void collectFileList(FileCollector collector, String fileListLocation) {
LOG.debug("Reading file list {}.", fileListLocation);
Path path = Paths.get(fileListLocation);
if (!Files.exists(path)) {
collector.getReporter().error("No such file {}", fileListLocation);
return;
}
String filePaths;
try {
filePaths = FileUtil.readFilelist(path.toFile());
} catch (IOException e) {
collector.getReporter().errorEx("Error reading {}", new Object[] { fileListLocation }, e);
return;
}
collectFiles(collector, filePaths);
}
private static void addRoot(FileCollector collector, String rootLocation) throws IOException {
Path path = Paths.get(rootLocation);
if (!Files.exists(path)) {
collector.getReporter().error("No such file {}", path);
return;
}
if (Files.isDirectory(path)) {
LOG.debug("Adding directory {}.", path);
collector.addDirectory(path);
} else if (rootLocation.endsWith(".zip") || rootLocation.endsWith(".jar")) {
LOG.debug("Adding zip file {}.", path);
@SuppressWarnings("PMD.CloseResource")
FileSystem fs = collector.addZipFile(path);
if (fs == null) {
return;
}
for (Path zipRoot : fs.getRootDirectories()) {
collector.addFileOrDirectory(zipRoot);
}
} else if (Files.isRegularFile(path)) {
LOG.debug("Adding regular file {}.", path);
collector.addFile(path);
} else {
LOG.debug("Ignoring {}: not a regular file or directory", path);
}
}
public static void collectDB(FileCollector collector, String uriString) {
try {
LOG.debug("Connecting to {}", uriString);
DBURI dbUri = new DBURI(uriString);
DBMSMetadata dbmsMetadata = new DBMSMetadata(dbUri);
LOG.trace("DBMSMetadata retrieved");
List<SourceObject> sourceObjectList = dbmsMetadata.getSourceObjectList();
LOG.trace("Located {} database source objects", sourceObjectList.size());
for (SourceObject sourceObject : sourceObjectList) {
String falseFilePath = sourceObject.getPseudoFileName();
LOG.trace("Adding database source object {}", falseFilePath);
try (Reader sourceCode = dbmsMetadata.getSourceCode(sourceObject)) {
String source = IOUtils.toString(sourceCode);
collector.addSourceFile(source, falseFilePath);
} catch (SQLException ex) {
collector.getReporter().warnEx("Cannot get SourceCode for {} - skipping ...",
new Object[] { falseFilePath },
ex);
}
}
} catch (ClassNotFoundException e) {
collector.getReporter().errorEx("Cannot get files from DB - probably missing database JDBC driver", e);
} catch (Exception e) {
collector.getReporter().errorEx("Cannot get files from DB - ''{}''", new Object[] { uriString }, e);
}
}
}

View File

@ -154,7 +154,7 @@ public abstract class BaseLanguageModule implements Language {
@Override
public String toString() {
return "LanguageModule:" + name + '(' + this.getClass().getSimpleName() + ')';
return getTerseName();
}
@Override

View File

@ -35,7 +35,7 @@ public class LanguageVersion implements Comparable<LanguageVersion> {
private final Language language;
private final String version;
private final LanguageVersionHandler languageVersionHandler;
private final LanguageVersionHandler languageVersionHandler; // note: this is null if this is a cpd-only language...
/**
* @deprecated Use {@link Language#getVersion(String)}. This is only

View File

@ -8,6 +8,11 @@ import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import net.sourceforge.pmd.internal.util.AssertionUtil;
/**
* This class can discover the LanguageVersion of a source file. Further, every
@ -17,6 +22,23 @@ import java.util.Map;
public class LanguageVersionDiscoverer {
private Map<Language, LanguageVersion> languageToLanguageVersion = new HashMap<>();
private LanguageVersion forcedVersion;
public LanguageVersionDiscoverer() {
this(null);
}
/**
* Build a new instance.
*
* @param forcedVersion If non-null, all files should be assigned this version.
* The methods of this class still work as usual and do not
* care about the forced language version.
*/
public LanguageVersionDiscoverer(LanguageVersion forcedVersion) {
this.forcedVersion = forcedVersion;
}
/**
* Set the given LanguageVersion as the current default for it's Language.
*
@ -25,6 +47,7 @@ public class LanguageVersionDiscoverer {
* @return The previous default version for the language.
*/
public LanguageVersion setDefaultLanguageVersion(LanguageVersion languageVersion) {
AssertionUtil.requireParamNotNull("languageVersion", languageVersion);
LanguageVersion currentLanguageVersion = languageToLanguageVersion.put(languageVersion.getLanguage(),
languageVersion);
if (currentLanguageVersion == null) {
@ -41,6 +64,7 @@ public class LanguageVersionDiscoverer {
* @return The current default version for the language.
*/
public LanguageVersion getDefaultLanguageVersion(Language language) {
Objects.requireNonNull(language);
LanguageVersion languageVersion = languageToLanguageVersion.get(language);
if (languageVersion == null) {
languageVersion = language.getDefaultVersion();
@ -81,6 +105,14 @@ public class LanguageVersionDiscoverer {
return languageVersion;
}
public LanguageVersion getForcedVersion() {
return forcedVersion;
}
public void setForcedVersion(LanguageVersion forceLanguageVersion) {
this.forcedVersion = forceLanguageVersion;
}
/**
* Get the Languages of a given source file.
*
@ -106,11 +138,8 @@ public class LanguageVersionDiscoverer {
// Get the extensions from a file
private String getExtension(String fileName) {
String extension = null;
int extensionIndex = 1 + fileName.lastIndexOf('.');
if (extensionIndex > 0) {
extension = fileName.substring(extensionIndex);
}
return extension;
return StringUtils.substringAfterLast(fileName, ".");
}
}

View File

@ -10,7 +10,7 @@ import java.util.Objects;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.properties.AbstractPropertySource;
import net.sourceforge.pmd.properties.PropertyDescriptor;
@ -72,7 +72,7 @@ public interface Parser {
public static final PropertyDescriptor<String> COMMENT_MARKER =
PropertyFactory.stringProperty("suppressionCommentMarker")
.desc("deprecated! NOPMD")
.defaultValue(PMD.SUPPRESS_MARKER)
.defaultValue(PMDConfiguration.DEFAULT_SUPPRESS_MARKER)
.build();
@Deprecated // transitional until language properties are implemented

Some files were not shown because too many files have changed in this diff Show More