diff --git a/.travis/release.sh b/.travis/release.sh index 2565125c88..9d468e4611 100755 --- a/.travis/release.sh +++ b/.travis/release.sh @@ -79,21 +79,30 @@ mkdir pmd.github.io git remote add origin git@github.com:pmd/pmd.github.io.git echo "latest/" > .git/info/sparse-checkout git pull --depth=1 origin master - log_info "Copying documentation from ../docs/pmd-doc-${RELEASE_VERSION}/ ..." + log_info "Copying documentation from ../docs/pmd-doc-${RELEASE_VERSION}/ to pmd-${RELEASE_VERSION}/ ..." rsync -ah --stats ../docs/pmd-doc-${RELEASE_VERSION}/ pmd-${RELEASE_VERSION}/ git status + echo "Executing: git add pmd-${RELEASE_VERSION}" git add pmd-${RELEASE_VERSION} + echo "Executing: git commit..." git commit -q -m "Added pmd-${RELEASE_VERSION}" + log_info "Copying pmd-${RELEASE_VERSION} to latest ..." git rm -qr latest cp -a pmd-${RELEASE_VERSION} latest + echo "Executing: git add latest" git add latest + echo "Executing: git commit..." git commit -q -m "Copying pmd-${RELEASE_VERSION} to latest" log_info "Generating sitemap.xml" ../.travis/sitemap_generator.sh > sitemap.xml + echo "Executing: git add sitemap.xml" git add sitemap.xml + echo "Executing: git commit..." git commit -q -m "Generated sitemap.xml" + + echo "Executing: git push origin master" git push origin master ) diff --git a/BUILDING.md b/BUILDING.md index b148affeab..d02537a33d 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -16,7 +16,7 @@ This will create the zip files in the directory `pmd-dist/target`: That's all ! -**Note:** While Java 9 is required for building, running PMD only requires Java 7 (or Java 8 for Apex). +**Note:** While Java 10 is required for building, running PMD only requires Java 7 (or Java 8 for Apex and the Designer). ## How to build the documentation? diff --git a/Dangerfile b/Dangerfile index 07a9aef9a2..daba7c415c 100644 --- a/Dangerfile +++ b/Dangerfile @@ -7,20 +7,14 @@ require 'logger' def run_pmdtester Dir.chdir('..') do argv = ['-r', './pmd', '-b', "#{ENV['TRAVIS_BRANCH']}", '-p', 'FETCH_HEAD', '-m', 'online', '-a'] - Process.fork do - begin - runner = PmdTester::Runner.new(argv) - introduce_new_pmd_errors = runner.run - warn("The PR may introduce new PMD errors!") if introduce_new_pmd_errors - rescue StandardError => e - warn("Running pmdtester failed, this message is mainly used to remind the maintainers of PMD.") - @logger.error "Running pmdtester failed: #{e.inspect}" - exit 1 - end - end - Process.wait - - upload_report if $?.success? + begin + runner = PmdTester::Runner.new(argv) + @new_errors, @removed_errors, @new_violations, @removed_violations = runner.run + upload_report + rescue StandardError => e + warn("Running pmdtester failed, this message is mainly used to remind the maintainers of PMD.") + @logger.error "Running pmdtester failed: #{e.inspect}" + end end end @@ -38,7 +32,8 @@ def upload_report @logger.info "Successfully uploaded #{tar_filename} to chunk.io" # set value of sticky to true and the message is kept after new commits are submited to the PR - message("Please check the [regression diff report](#{report_url.chomp}/diff/index.html) to make sure that everything is expected", sticky: true) + message("This changeset introduces #{@new_violations} new violations and #{@new_errors} new errors,\n" + + "removes #{@removed_violations} violations and #{@removed_errors} errors. [Full report](#{report_url.chomp}/diff/index.html)", sticky: true) else @logger.error "Error while uploading #{tar_filename} to chunk.io: #{report_url}" warn("Uploading the diff report failed, this message is mainly used to remind the maintainers of PMD.") diff --git a/Gemfile b/Gemfile index 4894455d47..2b9ab0d121 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org/' -gem 'pmdtester', '~> 1.0.0.pre.beta3' +gem 'pmdtester', :git => 'https://github.com/pmd/pmd-regression-tester.git' gem 'danger', '~> 5.6', '>= 5.6' # This group is only needed for rendering release notes diff --git a/do-release.sh b/do-release.sh index 38aab524e3..f52214ec7e 100755 --- a/do-release.sh +++ b/do-release.sh @@ -67,15 +67,6 @@ export DEVELOPMENT_VERSION export CURRENT_BRANCH -# install bundles needed for rendering release notes -bundle install --with=release_notes_preprocessing --path vendor/bundle - -RELEASE_RULESET="pmd-core/src/main/resources/rulesets/releases/${RELEASE_VERSION//\./}.xml" -export RELEASE_NOTES_POST="_posts/$(date -u +%Y-%m-%d)-PMD-${RELEASE_VERSION}.md" -echo "Generating ../pmd.github.io/${RELEASE_NOTES_POST}..." -NEW_RELEASE_NOTES=$(bundle exec .travis/render_release_notes.rb docs/pages/release_notes.md | tail -n +6) -echo "${NEW_RELEASE_NOTES}" > ../pmd.github.io/${RELEASE_NOTES_POST} - echo "* Update date info in **docs/_config.yml**." echo " date: $(date -u +%d-%B-%Y)" echo @@ -86,6 +77,22 @@ echo "* Update **../pmd.github.io/_config.yml** to mention the new release" echo echo "Press enter to continue..." read + +# install bundles needed for rendering release notes +bundle install --with=release_notes_preprocessing --path vendor/bundle + +RELEASE_RULESET="pmd-core/src/main/resources/rulesets/releases/${RELEASE_VERSION//\./}.xml" +export RELEASE_NOTES_POST="_posts/$(date -u +%Y-%m-%d)-PMD-${RELEASE_VERSION}.md" +echo "Generating ../pmd.github.io/${RELEASE_NOTES_POST}..." +NEW_RELEASE_NOTES=$(bundle exec .travis/render_release_notes.rb docs/pages/release_notes.md | tail -n +6) +cat > ../pmd.github.io/${RELEASE_NOTES_POST} < 0.5) concurrent-ruby (1.0.5) dnsruby (1.61.2) @@ -25,15 +25,15 @@ GEM ffi (>= 1.3.0) eventmachine (1.2.7) execjs (2.7.0) - faraday (0.15.2) + faraday (0.15.3) multipart-post (>= 1.2, < 3) ffi (1.9.25) forwardable-extended (2.6.0) gemoji (3.0.0) - github-pages (191) + github-pages (192) activesupport (= 4.2.10) github-pages-health-check (= 1.8.1) - jekyll (= 3.7.3) + jekyll (= 3.7.4) jekyll-avatar (= 0.6.0) jekyll-coffeescript (= 1.1.1) jekyll-commonmark-ghpages (= 0.1.5) @@ -87,7 +87,7 @@ GEM http_parser.rb (0.6.0) i18n (0.9.5) concurrent-ruby (~> 1.0) - jekyll (3.7.3) + jekyll (3.7.4) addressable (~> 2.4) colorator (~> 1.0) em-websocket (~> 0.5) @@ -207,7 +207,7 @@ GEM multipart-post (2.0.0) nokogiri (1.8.4) mini_portile2 (~> 2.3.0) - octokit (4.11.0) + octokit (4.12.0) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.1) forwardable-extended (~> 2.6) @@ -221,7 +221,7 @@ GEM ruby_dep (1.5.0) rubyzip (1.2.2) safe_yaml (1.0.4) - sass (3.5.7) + sass (3.6.0) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) diff --git a/docs/README.md b/docs/README.md index a183bdfa9a..d0aef2914f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,8 @@ -# PMD-New-Site -New Site For PMD Core Open Source Project +# PMD Documentation + +The documentation is available at: + +The documentation for the latest release is at: ## Site Theme @@ -7,6 +10,14 @@ This site was built using the tomjohnson1492/documentation-theme-jekyll theme A Jekyll-based theme designed for documentation and help systems. See the link for detailed instructions on setting up and configuring everything. http://idratherbewriting.com/documentation-theme-jekyll/ +## Building using Script + + bash build-docs.sh + +This will run bundler to fetch and potentially update the ruby gems. +And then it will execute jekyll and build a offline site. +Open the file `_site/index.html` with your browser to see the site. + ## Building using Bundler bundle install # once @@ -14,6 +25,9 @@ A Jekyll-based theme designed for documentation and help systems. See the link f Go to: http://localhost:4005/ +This variant is useful to get constant updates: When you modify a file, jekyll will automatically rebuild +the site, so you just need to hit Refresh in the browser to see the update. + ## Building using Docker docker build --no-cache -t pmd-doc . # once diff --git a/docs/_config.yml b/docs/_config.yml index aa5028435f..673000cf4e 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,8 +1,8 @@ repository: pmd/pmd pmd: - version: 6.8.0 - date: ??-??-2018 + version: 6.9.0 + date: ??-????-2018 release_type: minor output: web @@ -29,10 +29,16 @@ host: 127.0.0.1 port: 4005 # the port where the preview is rendered. You can leave this as is unless you have other Jekyll builds using this same port that might cause conflicts. in that case, use another port such as 4006. +# these are the files and directories that jekyll will exclude from the build exclude: - .idea/ - .gitignore -# these are the files and directories that jekyll will exclude from the build + - vendor/ + - Gemfile + - Gemfile.lock + - README.md + - Dockerfile + - build-docs.sh feedback_subject_line: PMD Source Code Analyzer diff --git a/docs/build-docs.sh b/docs/build-docs.sh new file mode 100644 index 0000000000..b105603075 --- /dev/null +++ b/docs/build-docs.sh @@ -0,0 +1,3 @@ +bundle install --path vendor/bundle +bundle update +bundle exec jekyll build diff --git a/docs/pages/pmd/rules/apex.md b/docs/pages/pmd/rules/apex.md index 7d1e5a2bc0..682964277b 100644 --- a/docs/pages/pmd/rules/apex.md +++ b/docs/pages/pmd/rules/apex.md @@ -113,7 +113,7 @@ folder: pmd/rules It contains the following rules: - [ApexBadCrypto](pmd_rules_apex_security.html#apexbadcrypto), [ApexCRUDViolation](pmd_rules_apex_security.html#apexcrudviolation), [ApexCSRF](pmd_rules_apex_security.html#apexcsrf), [ApexDangerousMethods](pmd_rules_apex_security.html#apexdangerousmethods), [ApexInsecureEndpoint](pmd_rules_apex_security.html#apexinsecureendpoint), [ApexOpenRedirect](pmd_rules_apex_security.html#apexopenredirect), [ApexSharingViolations](pmd_rules_apex_security.html#apexsharingviolations), [ApexSOQLInjection](pmd_rules_apex_security.html#apexsoqlinjection), [ApexSuggestUsingNamedCred](pmd_rules_apex_security.html#apexsuggestusingnamedcred), [ApexUnitTestClassShouldHaveAsserts](pmd_rules_apex_bestpractices.html#apexunittestclassshouldhaveasserts), [ApexUnitTestShouldNotUseSeeAllDataTrue](pmd_rules_apex_bestpractices.html#apexunittestshouldnotuseseealldatatrue), [ApexXSSFromEscapeFalse](pmd_rules_apex_security.html#apexxssfromescapefalse), [ApexXSSFromURLParam](pmd_rules_apex_security.html#apexxssfromurlparam), [AvoidDeeplyNestedIfStmts](pmd_rules_apex_design.html#avoiddeeplynestedifstmts), [AvoidDirectAccessTriggerMap](pmd_rules_apex_errorprone.html#avoiddirectaccesstriggermap), [AvoidDmlStatementsInLoops](pmd_rules_apex_performance.html#avoiddmlstatementsinloops), [AvoidGlobalModifier](pmd_rules_apex_bestpractices.html#avoidglobalmodifier), [AvoidHardcodingId](pmd_rules_apex_errorprone.html#avoidhardcodingid), [AvoidLogicInTrigger](pmd_rules_apex_bestpractices.html#avoidlogicintrigger), [AvoidNonExistentAnnotations](pmd_rules_apex_errorprone.html#avoidnonexistentannotations), [AvoidSoqlInLoops](pmd_rules_apex_performance.html#avoidsoqlinloops), [AvoidSoslInLoops](pmd_rules_apex_performance.html#avoidsoslinloops), [ClassNamingConventions](pmd_rules_apex_codestyle.html#classnamingconventions), [CyclomaticComplexity](pmd_rules_apex_design.html#cyclomaticcomplexity), [EmptyCatchBlock](pmd_rules_apex_errorprone.html#emptycatchblock), [EmptyIfStmt](pmd_rules_apex_errorprone.html#emptyifstmt), [EmptyStatementBlock](pmd_rules_apex_errorprone.html#emptystatementblock), [EmptyTryOrFinallyBlock](pmd_rules_apex_errorprone.html#emptytryorfinallyblock), [EmptyWhileStmt](pmd_rules_apex_errorprone.html#emptywhilestmt), [ExcessiveClassLength](pmd_rules_apex_design.html#excessiveclasslength), [ExcessiveParameterList](pmd_rules_apex_design.html#excessiveparameterlist), [ExcessivePublicCount](pmd_rules_apex_design.html#excessivepubliccount), [ForLoopsMustUseBraces](pmd_rules_apex_codestyle.html#forloopsmustusebraces), [IfElseStmtsMustUseBraces](pmd_rules_apex_codestyle.html#ifelsestmtsmustusebraces), [IfStmtsMustUseBraces](pmd_rules_apex_codestyle.html#ifstmtsmustusebraces), [MethodNamingConventions](pmd_rules_apex_codestyle.html#methodnamingconventions), [MethodWithSameNameAsEnclosingClass](pmd_rules_apex_errorprone.html#methodwithsamenameasenclosingclass), [NcssConstructorCount](pmd_rules_apex_design.html#ncssconstructorcount), [NcssMethodCount](pmd_rules_apex_design.html#ncssmethodcount), [NcssTypeCount](pmd_rules_apex_design.html#ncsstypecount), [OneDeclarationPerLine](pmd_rules_apex_codestyle.html#onedeclarationperline), [StdCyclomaticComplexity](pmd_rules_apex_design.html#stdcyclomaticcomplexity), [TooManyFields](pmd_rules_apex_design.html#toomanyfields), [VariableNamingConventions](pmd_rules_apex_codestyle.html#variablenamingconventions), [WhileLoopsMustUseBraces](pmd_rules_apex_codestyle.html#whileloopsmustusebraces) + [ApexBadCrypto](pmd_rules_apex_security.html#apexbadcrypto), [ApexCRUDViolation](pmd_rules_apex_security.html#apexcrudviolation), [ApexCSRF](pmd_rules_apex_security.html#apexcsrf), [ApexDangerousMethods](pmd_rules_apex_security.html#apexdangerousmethods), [ApexDoc](pmd_rules_apex_documentation.html#apexdoc), [ApexInsecureEndpoint](pmd_rules_apex_security.html#apexinsecureendpoint), [ApexOpenRedirect](pmd_rules_apex_security.html#apexopenredirect), [ApexSharingViolations](pmd_rules_apex_security.html#apexsharingviolations), [ApexSOQLInjection](pmd_rules_apex_security.html#apexsoqlinjection), [ApexSuggestUsingNamedCred](pmd_rules_apex_security.html#apexsuggestusingnamedcred), [ApexUnitTestClassShouldHaveAsserts](pmd_rules_apex_bestpractices.html#apexunittestclassshouldhaveasserts), [ApexUnitTestShouldNotUseSeeAllDataTrue](pmd_rules_apex_bestpractices.html#apexunittestshouldnotuseseealldatatrue), [ApexXSSFromEscapeFalse](pmd_rules_apex_security.html#apexxssfromescapefalse), [ApexXSSFromURLParam](pmd_rules_apex_security.html#apexxssfromurlparam), [AvoidDeeplyNestedIfStmts](pmd_rules_apex_design.html#avoiddeeplynestedifstmts), [AvoidDirectAccessTriggerMap](pmd_rules_apex_errorprone.html#avoiddirectaccesstriggermap), [AvoidDmlStatementsInLoops](pmd_rules_apex_performance.html#avoiddmlstatementsinloops), [AvoidGlobalModifier](pmd_rules_apex_bestpractices.html#avoidglobalmodifier), [AvoidHardcodingId](pmd_rules_apex_errorprone.html#avoidhardcodingid), [AvoidLogicInTrigger](pmd_rules_apex_bestpractices.html#avoidlogicintrigger), [AvoidNonExistentAnnotations](pmd_rules_apex_errorprone.html#avoidnonexistentannotations), [AvoidSoqlInLoops](pmd_rules_apex_performance.html#avoidsoqlinloops), [AvoidSoslInLoops](pmd_rules_apex_performance.html#avoidsoslinloops), [ClassNamingConventions](pmd_rules_apex_codestyle.html#classnamingconventions), [CyclomaticComplexity](pmd_rules_apex_design.html#cyclomaticcomplexity), [EmptyCatchBlock](pmd_rules_apex_errorprone.html#emptycatchblock), [EmptyIfStmt](pmd_rules_apex_errorprone.html#emptyifstmt), [EmptyStatementBlock](pmd_rules_apex_errorprone.html#emptystatementblock), [EmptyTryOrFinallyBlock](pmd_rules_apex_errorprone.html#emptytryorfinallyblock), [EmptyWhileStmt](pmd_rules_apex_errorprone.html#emptywhilestmt), [ExcessiveClassLength](pmd_rules_apex_design.html#excessiveclasslength), [ExcessiveParameterList](pmd_rules_apex_design.html#excessiveparameterlist), [ExcessivePublicCount](pmd_rules_apex_design.html#excessivepubliccount), [ForLoopsMustUseBraces](pmd_rules_apex_codestyle.html#forloopsmustusebraces), [IfElseStmtsMustUseBraces](pmd_rules_apex_codestyle.html#ifelsestmtsmustusebraces), [IfStmtsMustUseBraces](pmd_rules_apex_codestyle.html#ifstmtsmustusebraces), [MethodNamingConventions](pmd_rules_apex_codestyle.html#methodnamingconventions), [MethodWithSameNameAsEnclosingClass](pmd_rules_apex_errorprone.html#methodwithsamenameasenclosingclass), [NcssConstructorCount](pmd_rules_apex_design.html#ncssconstructorcount), [NcssMethodCount](pmd_rules_apex_design.html#ncssmethodcount), [NcssTypeCount](pmd_rules_apex_design.html#ncsstypecount), [OneDeclarationPerLine](pmd_rules_apex_codestyle.html#onedeclarationperline), [StdCyclomaticComplexity](pmd_rules_apex_design.html#stdcyclomaticcomplexity), [TooManyFields](pmd_rules_apex_design.html#toomanyfields), [VariableNamingConventions](pmd_rules_apex_codestyle.html#variablenamingconventions), [WhileLoopsMustUseBraces](pmd_rules_apex_codestyle.html#whileloopsmustusebraces) * Empty Code (`rulesets/apex/empty.xml`): diff --git a/docs/pages/pmd/rules/java.md b/docs/pages/pmd/rules/java.md index e3dbda321f..516e0ddd7a 100644 --- a/docs/pages/pmd/rules/java.md +++ b/docs/pages/pmd/rules/java.md @@ -250,7 +250,7 @@ folder: pmd/rules * [MethodWithSameNameAsEnclosingClass](pmd_rules_java_errorprone.html#methodwithsamenameasenclosingclass): Non-constructor methods should not have the same name as the enclosing class. * [MisplacedNullCheck](pmd_rules_java_errorprone.html#misplacednullcheck): The null check here is misplaced. If the variable is null a NullPointerException will be thrown.E... * [MissingBreakInSwitch](pmd_rules_java_errorprone.html#missingbreakinswitch): Switch statements without break or return statements for each case optionmay indicate problematic... -* [MissingSerialVersionUID](pmd_rules_java_errorprone.html#missingserialversionuid): Serializable classes should provide a serialVersionUID field. +* [MissingSerialVersionUID](pmd_rules_java_errorprone.html#missingserialversionuid): Serializable classes should provide a serialVersionUID field.The serialVersionUID field is also n... * [MissingStaticMethodInNonInstantiatableClass](pmd_rules_java_errorprone.html#missingstaticmethodinnoninstantiatableclass): A class that has private constructors and does not have any static methods or fields cannot be used. * [MoreThanOneLogger](pmd_rules_java_errorprone.html#morethanonelogger): Normally only one logger is used in each class. * [NonCaseLabelInSwitchStatement](pmd_rules_java_errorprone.html#noncaselabelinswitchstatement): A non-case label (e.g. a named break/continue label) was present in a switch statement.This legal... @@ -524,6 +524,14 @@ folder: pmd/rules [AddEmptyString](pmd_rules_java_performance.html#addemptystring), [AvoidArrayLoops](pmd_rules_java_performance.html#avoidarrayloops), [AvoidInstantiatingObjectsInLoops](pmd_rules_java_performance.html#avoidinstantiatingobjectsinloops), [LocalVariableCouldBeFinal](pmd_rules_java_codestyle.html#localvariablecouldbefinal), [MethodArgumentCouldBeFinal](pmd_rules_java_codestyle.html#methodargumentcouldbefinal), [PrematureDeclaration](pmd_rules_java_codestyle.html#prematuredeclaration), [RedundantFieldInitializer](pmd_rules_java_performance.html#redundantfieldinitializer), [SimplifyStartsWith](pmd_rules_java_performance.html#simplifystartswith), [UnnecessaryWrapperObjectCreation](pmd_rules_java_performance.html#unnecessarywrapperobjectcreation), [UseArrayListInsteadOfVector](pmd_rules_java_performance.html#usearraylistinsteadofvector), [UseArraysAsList](pmd_rules_java_performance.html#usearraysaslist), [UseStringBufferForStringAppends](pmd_rules_java_performance.html#usestringbufferforstringappends) +* quickstart (`rulesets/java/quickstart.xml`): + + Quickstart configuration of PMD. Includes the rules that are most likely to apply everywhere. + + It contains the following rules: + + [AbstractClassWithoutAnyMethod](pmd_rules_java_design.html#abstractclasswithoutanymethod), [AssignmentInOperand](pmd_rules_java_errorprone.html#assignmentinoperand), [AssignmentToNonFinalStatic](pmd_rules_java_errorprone.html#assignmenttononfinalstatic), [AvoidAccessibilityAlteration](pmd_rules_java_errorprone.html#avoidaccessibilityalteration), [AvoidBranchingStatementAsLastInLoop](pmd_rules_java_errorprone.html#avoidbranchingstatementaslastinloop), [AvoidCatchingThrowable](pmd_rules_java_errorprone.html#avoidcatchingthrowable), [AvoidDecimalLiteralsInBigDecimalConstructor](pmd_rules_java_errorprone.html#avoiddecimalliteralsinbigdecimalconstructor), [AvoidDollarSigns](pmd_rules_java_codestyle.html#avoiddollarsigns), [AvoidInstanceofChecksInCatchClause](pmd_rules_java_errorprone.html#avoidinstanceofchecksincatchclause), [AvoidMultipleUnaryOperators](pmd_rules_java_errorprone.html#avoidmultipleunaryoperators), [AvoidProtectedFieldInFinalClass](pmd_rules_java_codestyle.html#avoidprotectedfieldinfinalclass), [AvoidProtectedMethodInFinalClassNotExtending](pmd_rules_java_codestyle.html#avoidprotectedmethodinfinalclassnotextending), [AvoidStringBufferField](pmd_rules_java_bestpractices.html#avoidstringbufferfield), [AvoidThreadGroup](pmd_rules_java_multithreading.html#avoidthreadgroup), [AvoidUsingHardCodedIP](pmd_rules_java_bestpractices.html#avoidusinghardcodedip), [AvoidUsingOctalValues](pmd_rules_java_errorprone.html#avoidusingoctalvalues), [AvoidUsingVolatile](pmd_rules_java_multithreading.html#avoidusingvolatile), [BadComparison](pmd_rules_java_errorprone.html#badcomparison), [BigIntegerInstantiation](pmd_rules_java_performance.html#bigintegerinstantiation), [BooleanInstantiation](pmd_rules_java_performance.html#booleaninstantiation), [BrokenNullCheck](pmd_rules_java_errorprone.html#brokennullcheck), [CheckResultSet](pmd_rules_java_bestpractices.html#checkresultset), [CheckSkipResult](pmd_rules_java_errorprone.html#checkskipresult), [ClassCastExceptionWithToArray](pmd_rules_java_errorprone.html#classcastexceptionwithtoarray), [ClassNamingConventions](pmd_rules_java_codestyle.html#classnamingconventions), [ClassWithOnlyPrivateConstructorsShouldBeFinal](pmd_rules_java_design.html#classwithonlyprivateconstructorsshouldbefinal), [CloneMethodMustBePublic](pmd_rules_java_errorprone.html#clonemethodmustbepublic), [CloneMethodMustImplementCloneable](pmd_rules_java_errorprone.html#clonemethodmustimplementcloneable), [CloneMethodReturnTypeMustMatchClassName](pmd_rules_java_errorprone.html#clonemethodreturntypemustmatchclassname), [CloneThrowsCloneNotSupportedException](pmd_rules_java_errorprone.html#clonethrowsclonenotsupportedexception), [CloseResource](pmd_rules_java_errorprone.html#closeresource), [CompareObjectsWithEquals](pmd_rules_java_errorprone.html#compareobjectswithequals), [ConstantsInInterface](pmd_rules_java_bestpractices.html#constantsininterface), [ControlStatementBraces](pmd_rules_java_codestyle.html#controlstatementbraces), [DefaultLabelNotLastInSwitchStmt](pmd_rules_java_bestpractices.html#defaultlabelnotlastinswitchstmt), [DoNotCallGarbageCollectionExplicitly](pmd_rules_java_errorprone.html#donotcallgarbagecollectionexplicitly), [DoNotExtendJavaLangError](pmd_rules_java_design.html#donotextendjavalangerror), [DoNotExtendJavaLangThrowable](pmd_rules_java_errorprone.html#donotextendjavalangthrowable), [DontCallThreadRun](pmd_rules_java_multithreading.html#dontcallthreadrun), [DontImportJavaLang](pmd_rules_java_codestyle.html#dontimportjavalang), [DontUseFloatTypeForLoopIndices](pmd_rules_java_errorprone.html#dontusefloattypeforloopindices), [DoubleCheckedLocking](pmd_rules_java_multithreading.html#doublecheckedlocking), [DuplicateImports](pmd_rules_java_codestyle.html#duplicateimports), [EmptyCatchBlock](pmd_rules_java_errorprone.html#emptycatchblock), [EmptyFinalizer](pmd_rules_java_errorprone.html#emptyfinalizer), [EmptyFinallyBlock](pmd_rules_java_errorprone.html#emptyfinallyblock), [EmptyIfStmt](pmd_rules_java_errorprone.html#emptyifstmt), [EmptyInitializer](pmd_rules_java_errorprone.html#emptyinitializer), [EmptyStatementBlock](pmd_rules_java_errorprone.html#emptystatementblock), [EmptyStatementNotInLoop](pmd_rules_java_errorprone.html#emptystatementnotinloop), [EmptySwitchStatements](pmd_rules_java_errorprone.html#emptyswitchstatements), [EmptySynchronizedBlock](pmd_rules_java_errorprone.html#emptysynchronizedblock), [EmptyTryBlock](pmd_rules_java_errorprone.html#emptytryblock), [EmptyWhileStmt](pmd_rules_java_errorprone.html#emptywhilestmt), [EqualsNull](pmd_rules_java_errorprone.html#equalsnull), [ExtendsObject](pmd_rules_java_codestyle.html#extendsobject), [FinalFieldCouldBeStatic](pmd_rules_java_design.html#finalfieldcouldbestatic), [ForLoopCanBeForeach](pmd_rules_java_bestpractices.html#forloopcanbeforeach), [ForLoopShouldBeWhileLoop](pmd_rules_java_codestyle.html#forloopshouldbewhileloop), [FormalParameterNamingConventions](pmd_rules_java_codestyle.html#formalparameternamingconventions), [GenericsNaming](pmd_rules_java_codestyle.html#genericsnaming), [GuardLogStatement](pmd_rules_java_bestpractices.html#guardlogstatement), [IdempotentOperations](pmd_rules_java_errorprone.html#idempotentoperations), [IdenticalCatchBranches](pmd_rules_java_codestyle.html#identicalcatchbranches), [ImportFromSamePackage](pmd_rules_java_errorprone.html#importfromsamepackage), [InstantiationToGetClass](pmd_rules_java_errorprone.html#instantiationtogetclass), [JumbledIncrementer](pmd_rules_java_errorprone.html#jumbledincrementer), [LocalVariableNamingConventions](pmd_rules_java_codestyle.html#localvariablenamingconventions), [LogicInversion](pmd_rules_java_design.html#logicinversion), [LooseCoupling](pmd_rules_java_bestpractices.html#loosecoupling), [MethodNamingConventions](pmd_rules_java_codestyle.html#methodnamingconventions), [MisplacedNullCheck](pmd_rules_java_errorprone.html#misplacednullcheck), [MissingBreakInSwitch](pmd_rules_java_errorprone.html#missingbreakinswitch), [MissingOverride](pmd_rules_java_bestpractices.html#missingoverride), [MissingStaticMethodInNonInstantiatableClass](pmd_rules_java_errorprone.html#missingstaticmethodinnoninstantiatableclass), [NonCaseLabelInSwitchStatement](pmd_rules_java_errorprone.html#noncaselabelinswitchstatement), [NonStaticInitializer](pmd_rules_java_errorprone.html#nonstaticinitializer), [NonThreadSafeSingleton](pmd_rules_java_multithreading.html#nonthreadsafesingleton), [NoPackage](pmd_rules_java_codestyle.html#nopackage), [OneDeclarationPerLine](pmd_rules_java_bestpractices.html#onedeclarationperline), [OptimizableToArrayCall](pmd_rules_java_performance.html#optimizabletoarraycall), [OverrideBothEqualsAndHashcode](pmd_rules_java_errorprone.html#overridebothequalsandhashcode), [PackageCase](pmd_rules_java_codestyle.html#packagecase), [PositionLiteralsFirstInCaseInsensitiveComparisons](pmd_rules_java_bestpractices.html#positionliteralsfirstincaseinsensitivecomparisons), [PositionLiteralsFirstInComparisons](pmd_rules_java_bestpractices.html#positionliteralsfirstincomparisons), [PreserveStackTrace](pmd_rules_java_bestpractices.html#preservestacktrace), [ProperCloneImplementation](pmd_rules_java_errorprone.html#propercloneimplementation), [ProperLogger](pmd_rules_java_errorprone.html#properlogger), [ReturnEmptyArrayRatherThanNull](pmd_rules_java_errorprone.html#returnemptyarrayratherthannull), [ReturnFromFinallyBlock](pmd_rules_java_errorprone.html#returnfromfinallyblock), [SimplifiedTernary](pmd_rules_java_design.html#simplifiedternary), [SimplifyBooleanReturns](pmd_rules_java_design.html#simplifybooleanreturns), [SimplifyConditional](pmd_rules_java_design.html#simplifyconditional), [SingleMethodSingleton](pmd_rules_java_errorprone.html#singlemethodsingleton), [SingletonClassReturningNewInstance](pmd_rules_java_errorprone.html#singletonclassreturningnewinstance), [SingularField](pmd_rules_java_design.html#singularfield), [SuspiciousEqualsMethodName](pmd_rules_java_errorprone.html#suspiciousequalsmethodname), [SuspiciousHashcodeMethodName](pmd_rules_java_errorprone.html#suspicioushashcodemethodname), [SuspiciousOctalEscape](pmd_rules_java_errorprone.html#suspiciousoctalescape), [SwitchStmtsShouldHaveDefault](pmd_rules_java_bestpractices.html#switchstmtsshouldhavedefault), [UncommentedEmptyConstructor](pmd_rules_java_documentation.html#uncommentedemptyconstructor), [UncommentedEmptyMethodBody](pmd_rules_java_documentation.html#uncommentedemptymethodbody), [UnconditionalIfStatement](pmd_rules_java_errorprone.html#unconditionalifstatement), [UnnecessaryAnnotationValueElement](pmd_rules_java_codestyle.html#unnecessaryannotationvalueelement), [UnnecessaryConstructor](pmd_rules_java_codestyle.html#unnecessaryconstructor), [UnnecessaryConversionTemporary](pmd_rules_java_errorprone.html#unnecessaryconversiontemporary), [UnnecessaryFullyQualifiedName](pmd_rules_java_codestyle.html#unnecessaryfullyqualifiedname), [UnnecessaryLocalBeforeReturn](pmd_rules_java_codestyle.html#unnecessarylocalbeforereturn), [UnnecessaryModifier](pmd_rules_java_codestyle.html#unnecessarymodifier), [UnnecessaryReturn](pmd_rules_java_codestyle.html#unnecessaryreturn), [UnsynchronizedStaticDateFormatter](pmd_rules_java_multithreading.html#unsynchronizedstaticdateformatter), [UnusedFormalParameter](pmd_rules_java_bestpractices.html#unusedformalparameter), [UnusedImports](pmd_rules_java_bestpractices.html#unusedimports), [UnusedLocalVariable](pmd_rules_java_bestpractices.html#unusedlocalvariable), [UnusedNullCheckInEquals](pmd_rules_java_errorprone.html#unusednullcheckinequals), [UnusedPrivateField](pmd_rules_java_bestpractices.html#unusedprivatefield), [UnusedPrivateMethod](pmd_rules_java_bestpractices.html#unusedprivatemethod), [UseAssertEqualsInsteadOfAssertTrue](pmd_rules_java_bestpractices.html#useassertequalsinsteadofasserttrue), [UseAssertNullInsteadOfAssertTrue](pmd_rules_java_bestpractices.html#useassertnullinsteadofasserttrue), [UseAssertSameInsteadOfAssertTrue](pmd_rules_java_bestpractices.html#useassertsameinsteadofasserttrue), [UseAssertTrueInsteadOfAssertEquals](pmd_rules_java_bestpractices.html#useasserttrueinsteadofassertequals), [UseCollectionIsEmpty](pmd_rules_java_bestpractices.html#usecollectionisempty), [UseEqualsToCompareStrings](pmd_rules_java_errorprone.html#useequalstocomparestrings), [UselessOperationOnImmutable](pmd_rules_java_errorprone.html#uselessoperationonimmutable), [UselessOverridingMethod](pmd_rules_java_design.html#uselessoverridingmethod), [UselessParentheses](pmd_rules_java_codestyle.html#uselessparentheses), [UselessQualifiedThis](pmd_rules_java_codestyle.html#uselessqualifiedthis), [UseLocaleWithCaseConversions](pmd_rules_java_errorprone.html#uselocalewithcaseconversions), [UseNotifyAllInsteadOfNotify](pmd_rules_java_multithreading.html#usenotifyallinsteadofnotify), [UseUtilityClass](pmd_rules_java_design.html#useutilityclass) + * Security Code Guidelines (`rulesets/java/sunsecure.xml`): Deprecated This ruleset is for backwards compatibility. diff --git a/docs/pages/pmd/rules/java/errorprone.md b/docs/pages/pmd/rules/java/errorprone.md index 225226cc72..b81d6b9f20 100644 --- a/docs/pages/pmd/rules/java/errorprone.md +++ b/docs/pages/pmd/rules/java/errorprone.md @@ -2409,11 +2409,13 @@ public void bar(int status) { **Priority:** Medium (3) Serializable classes should provide a serialVersionUID field. +The serialVersionUID field is also needed for abstract base classes. Each individual class in the inheritance +chain needs an own serialVersionUID field. See also [Should an abstract class have a serialVersionUID](https://stackoverflow.com/questions/893259/should-an-abstract-class-have-a-serialversionuid). **This rule is defined by the following XPath expression:** ``` xpath //ClassOrInterfaceDeclaration - [@Abstract = 'false'] + [@Interface = 'false'] [count(ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration /FieldDeclaration/VariableDeclarator/VariableDeclaratorId[@Image='serialVersionUID']) = 0] [(ImplementsList | ExtendsList)/ClassOrInterfaceType[pmd-java:typeIs('java.io.Serializable')]] diff --git a/docs/pages/pmd/userdocs/installation.md b/docs/pages/pmd/userdocs/installation.md index fc9d31ab32..4471574266 100644 --- a/docs/pages/pmd/userdocs/installation.md +++ b/docs/pages/pmd/userdocs/installation.md @@ -50,9 +50,9 @@ modifiers on Java sources with `-R category/java/codestyle.xml/UnnecessaryModifi {% include note.html content="At the moment the formerly provided rulesets (eg `rulesets/java/basic.xml`) are deprecated, - though you can still use them. PMD will soon include standard rulesets as default configurations, - but you're strongly encouraged to [create your own ruleset](pmd_userdocs_making_rulesets.html) from - the start." %} + though you can still use them. PMD includes a quickstart ruleset for some languages (currently, Java) + as base configurations, which you can reference as e.g. `rulesets/java/quickstart.xml`. You're strongly + encouraged to [create your own ruleset](pmd_userdocs_making_rulesets.html) from the start though." %} Additionally, the following options, are specified most of the time even though they're not required: * `-f `: report format. PMD supports many report formats out of the box. You may want to start with the basic diff --git a/docs/pages/pmd/userdocs/tools/tools.md b/docs/pages/pmd/userdocs/tools/tools.md index 1a15363cc9..37b9e32943 100644 --- a/docs/pages/pmd/userdocs/tools/tools.md +++ b/docs/pages/pmd/userdocs/tools/tools.md @@ -6,11 +6,19 @@ permalink: pmd_userdocs_tools.html author: David Dixon-Peugh --- -* IDE plugins -* [Continuous Integrations plugins](/pmd_userdocs_ci.html) -* GUIs +## Automated Code Review +### Codacy +[Codacy](https://www.codacy.com/) automates code reviews and monitors code quality on every commit and pull request. +It gives visibility into the technical debt and it can track code style and security issues, code coverage, code duplication, cyclomatic complexity and enforce best practices. +Codacy is static analysis without the hassle. + +With Codacy you have PMDJava analysis out-of-the-box, and it is free for open source projects. + +* Homepage: [https://www.codacy.com/](https://www.codacy.com/) +* Source code: [https://github.com/codacy/codacy-pmdjava](https://github.com/codacy/codacy-pmdjava) +* Maintainer: Codacy ## IDE Integrations @@ -31,13 +39,6 @@ author: David Dixon-Peugh Tom Copeland - - Codacy - - github: codacy/codacy-pmdjava - Codacy - - CodeGuide @@ -167,12 +168,6 @@ the [PMDExtension jar file](http://sourceforge.net/projects/pmd/files/pmd-bluej/ and place it in your `bluej/lib/extensions/` directory. -### Codacy - -Although it is not an IDE, with [Codacy](https://www.codacy.com/) you have PMDJava analysis out-of-the-box, -and it is free for open source projects. - - ### Code Guide Here's how to set up PMD with Omnicore's CodeGuide: diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 72fe9a52a7..9e8a8fab75 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -10,21 +10,21 @@ The PMD team is pleased to announce PMD {{ site.pmd.version }}. This is a {{ site.pmd.release_type }} release. -{% tocmaker %} +{% tocmaker is_release_notes_processor %} ### New and noteworthy ### Fixed Issues * java-codestyle - * [#1329](https://github.com/pmd/pmd/issues/1329): \[java] FieldNamingConventions: false positive in serializable class with serialVersionUID - * [#1334](https://github.com/pmd/pmd/issues/1334): \[java] LinguisticNaming should support AtomicBooleans -* java-performance - * [#1325](https://github.com/pmd/pmd/issues/1325): \[java] False positive in ConsecutiveLiteralAppends + * [#1356](https://github.com/pmd/pmd/issues/1356): \[java] UnnecessaryModifier wrong message public->static ### API Changes ### External Contributions +* [#1366](https://github.com/pmd/pmd/pull/1366): \[Java] Static Modifier on Internal Interface pmd #1356 - [avishvat](https://github.com/vishva007) +* [#1368](https://github.com/pmd/pmd/pull/1368): \[doc] Updated outdated note in the building documentation. - [Maikel Steneker](https://github.com/maikelsteneker) + {% endtocmaker %} diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index de6639ce49..52f115524a 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -6,6 +6,160 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: http://sourceforge.net/projects/pmd/files/pmd/ +## 30-September-2018 - 6.8.0 + +The PMD team is pleased to announce PMD 6.8.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [Drawing a line between private and public API](#drawing-a-line-between-private-and-public-api) + * [`.internal` packages and `@InternalApi` annotation](#`.internal`-packages-and-`@internalapi`-annotation) + * [`@ReservedSubclassing`](#`@reservedsubclassing`) + * [`@Experimental`](#`@experimental`) + * [`@Deprecated`](#`@deprecated`) + * [The transition](#the-transition) + * [Quickstart Ruleset](#quickstart-ruleset) + * [New Rules](#new-rules) + * [Modified Rules](#modified-rules) + * [PLSQL](#plsql) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) +* [External Contributions](#external-contributions) + +### New and noteworthy + +#### Drawing a line between private and public API + +Until now, all released public members and types were implicitly considered part +of PMD's public API, including inheritance-specific members (protected members, abstract methods). +We have maintained those APIs with the goal to preserve full binary compatibility between minor releases, +only breaking those APIs infrequently, for major releases. + +In order to allow PMD to move forward at a faster pace, this implicit contract will +be invalidated with PMD 7.0.0. We now introduce more fine-grained distinctions between +the type of compatibility support we guarantee for our libraries, and ways to make +them explicit to clients of PMD. + +###### `.internal` packages and `@InternalApi` annotation + +*Internal API* is meant for use *only* by the main PMD codebase. Internal types and methods +may be modified in any way, or even removed, at any time. + +Any API in a package that contains an `.internal` segment is considered internal. +The `@InternalApi` annotation will be used for APIs that have to live outside of +these packages, e.g. methods of a public type that shouldn't be used outside of PMD (again, +these can be removed anytime). + +###### `@ReservedSubclassing` + +Types marked with the `@ReservedSubclassing` annotation are only meant to be subclassed +by classes within PMD. As such, we may add new abstract methods, or remove protected methods, +at any time. All published public members remain supported. The annotation is *not* inherited, which +means a reserved interface doesn't prevent its implementors to be subclassed. + +###### `@Experimental` + +APIs marked with the `@Experimental` annotation at the class or method level are subject to change. +They can be modified in any way, or even removed, at any time. You should not use or rely + on them in any production code. They are purely to allow broad testing and feedback. + +###### `@Deprecated` + +APIs marked with the `@Deprecated` annotation at the class or method level will remain supported +until the next major release but it is recommended to stop using them. + +###### The transition + +*All currently supported APIs will remain so until 7.0.0*. All APIs that are to be moved to +`.internal` packages or hidden will be tagged `@InternalApi` before that major release, and +the breaking API changes will be performed in 7.0.0. + + +#### Quickstart Ruleset + +PMD 6.8.0 provides a first quickstart ruleset for Java, which you can use as a base ruleset to get your +custom ruleset started. You can reference it with `rulesets/java/quickstart.xml`. +You are strongly encouraged to [create your own ruleset](https://pmd.github.io/pmd-6.7.0/pmd_userdocs_making_rulesets.html) +though. + +The quickstart ruleset has the intention, to be useful out-of-the-box for many projects. Therefore it +references only rules, that are most likely to apply everywhere. + +Any feedback would be greatly appreciated. + + +#### New Rules + +* The new Apex rule [`ApexDoc`](https://pmd.github.io/pmd-6.8.0/pmd_rules_apex_documentation.html#apexdoc) (`apex-documentation`) + enforces the inclusion of ApexDoc on classes, interfaces, properties and methods; as well as some + sanity rules for such docs (no missing parameters, parameters' order, and return value). By default, + method overrides and test classes are allowed to not include ApexDoc. + + +#### Modified Rules + +* The rule [`MissingSerialVersionUID`](https://pmd.github.io/pmd-6.8.0/pmd_rules_java_errorprone.html#missingserialversionuid) (`java-errorprone`) has been modified + in order to recognize also missing `serialVersionUID` fields in abstract classes, if they are serializable. + Each individual class in the inheritance chain needs an own serialVersionUID field. See also [Should an abstract class have a serialVersionUID](https://stackoverflow.com/questions/893259/should-an-abstract-class-have-a-serialversionuid). + This change might lead to additional violations in existing code bases. + + +#### PLSQL + +The grammar for PLSQL has been revamped in order to fully parse `SELECT INTO`, `UPDATE`, and `DELETE` +statements. Previously such statements have been simply skipped ahead, now PMD is parsing them, giving access +to the individual parts of a SELECT-statement, such as the Where-Clause. This might produce new parsing errors +where PMD previously could successfully parse PLSQL code. If this happens, please report a new [issue](https://github.com/pmd/pmd/issues/new) to get this problem fixed. + + +### Fixed Issues + +* apex-bestpractices + * [#1348](https://github.com/pmd/pmd/issues/1348): \[apex] AvoidGlobalModifierRule gives warning even when its a webservice - false positive +* java-codestyle + * [#1329](https://github.com/pmd/pmd/issues/1329): \[java] FieldNamingConventions: false positive in serializable class with serialVersionUID + * [#1334](https://github.com/pmd/pmd/issues/1334): \[java] LinguisticNaming should support AtomicBooleans +* java-errorprone + * [#1350](https://github.com/pmd/pmd/issues/1350): \[java] MissingSerialVersionUID false-positive on interfaces + * [#1352](https://github.com/pmd/pmd/issues/1352): \[java] MissingSerialVersionUID false-negative with abstract classes +* java-performance + * [#1325](https://github.com/pmd/pmd/issues/1325): \[java] False positive in ConsecutiveLiteralAppends +* plsql + * [#1279](https://github.com/pmd/pmd/pull/1279): \[plsql] Support for SELECT INTO + + +### API Changes + +* A couple of methods and fields in `net.sourceforge.pmd.properties.AbstractPropertySource` have been + deprecated, as they are replaced by already existing functionality or expose internal implementation + details: `propertyDescriptors`, `propertyValuesByDescriptor`, + `copyPropertyDescriptors()`, `copyPropertyValues()`, `ignoredProperties()`, `usesDefaultValues()`, + `useDefaultValueFor()`. + +* Some methods in `net.sourceforge.pmd.properties.PropertySource` have been deprecated as well: + `usesDefaultValues()`, `useDefaultValueFor()`, `ignoredProperties()`. + +* The class `net.sourceforge.pmd.lang.rule.AbstractDelegateRule` has been deprecated and will + be removed with PMD 7.0.0. It is internally only in use by RuleReference. + +* The default constructor of `net.sourceforge.pmd.lang.rule.RuleReference` has been deprecated + and will be removed with PMD 7.0.0. RuleReferences should only be created by providing a Rule and + a RuleSetReference. Furthermore the following methods are deprecated: `setRuleReference()`, + `hasOverriddenProperty()`, `usesDefaultValues()`, `useDefaultValueFor()`. + + +### External Contributions + +* [#1309](https://github.com/pmd/pmd/pull/1309): \[core] \[CPD] Decouple Antlr Tokenizer implementation from any CPD language supported with Antlr - [Matías Fraga](https://github.com/matifraga) +* [#1314](https://github.com/pmd/pmd/pull/1314): \[apex] Add validation of ApexDoc comments - [Jeff Hube](https://github.com/jeffhube) +* [#1339](https://github.com/pmd/pmd/pull/1339): \[ci] Improve danger message - [BBG](https://github.com/djydewang) +* [#1340](https://github.com/pmd/pmd/pull/1340): \[java] Derive correct classname for non-public non-classes - [kris-scheibe](https://github.com/kris-scheibe) +* [#1357](https://github.com/pmd/pmd/pull/1357): \[doc] Improve Codacy description - [Daniel Reigada](https://github.com/DReigada) + + ## 02-September-2018 - 6.7.0 The PMD team is pleased to announce PMD 6.7.0. diff --git a/docs/update.sh b/docs/update.sh deleted file mode 100755 index 42a3bece57..0000000000 --- a/docs/update.sh +++ /dev/null @@ -1,4 +0,0 @@ -git add . -git status -git commit -m "content update" -git push \ No newline at end of file diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml index a6897a5657..371b80e407 100644 --- a/pmd-apex-jorje/pom.xml +++ b/pmd-apex-jorje/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index b9154e5ad6..23ef98ac83 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTFormalComment.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTFormalComment.java new file mode 100644 index 0000000000..afdde670cf --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTFormalComment.java @@ -0,0 +1,23 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +public class ASTFormalComment extends AbstractApexNodeBase { + private String token; + + public ASTFormalComment(String token) { + super(ASTFormalComment.class); + this.token = token; + } + + @Override + Object jjtAccept(ApexParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + public String getToken() { + return token; + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNode.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNode.java index 73dd3fad07..bc18a8d05d 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNode.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNode.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.lang.apex.ast; -import net.sourceforge.pmd.lang.ast.AbstractNode; -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.SourceCodePositioner; import apex.jorje.data.Location; @@ -13,12 +11,12 @@ import apex.jorje.data.Locations; import apex.jorje.semantic.ast.AstNode; import apex.jorje.semantic.exception.UnexpectedCodePathException; -public abstract class AbstractApexNode extends AbstractNode implements ApexNode { +public abstract class AbstractApexNode extends AbstractApexNodeBase implements ApexNode { protected final T node; public AbstractApexNode(T node) { - super(node.getClass().hashCode()); + super(node.getClass()); this.node = node; } @@ -28,90 +26,13 @@ public abstract class AbstractApexNode extends AbstractNode i } Location loc = node.getLoc(); - int startOffset = loc.getStartIndex(); - int endOffset = loc.getEndIndex(); - // end column will be interpreted as inclusive, while endOffset/endIndex - // is exclusive - endOffset -= 1; - - this.beginLine = positioner.lineNumberFromOffset(startOffset); - this.beginColumn = positioner.columnFromOffset(this.beginLine, startOffset); - this.endLine = positioner.lineNumberFromOffset(endOffset); - this.endColumn = positioner.columnFromOffset(this.endLine, endOffset); - - if (this.endColumn < 0) { - this.endColumn = 0; - } + calculateLineNumbers(positioner, loc.getStartIndex(), loc.getEndIndex()); } protected void handleSourceCode(String source) { // default implementation does nothing } - @Override - public int getBeginLine() { - if (this.beginLine > 0) { - return this.beginLine; - } - Node parent = jjtGetParent(); - if (parent != null) { - return parent.getBeginLine(); - } - throw new RuntimeException("Unable to determine beginning line of Node."); - } - - @Override - public int getBeginColumn() { - if (this.beginColumn > 0) { - return this.beginColumn; - } - Node parent = jjtGetParent(); - if (parent != null) { - return parent.getBeginColumn(); - } - throw new RuntimeException("Unable to determine beginning column of Node."); - } - - @Override - public int getEndLine() { - if (this.endLine > 0) { - return this.endLine; - } - Node parent = jjtGetParent(); - if (parent != null) { - return parent.getEndLine(); - } - throw new RuntimeException("Unable to determine ending line of Node."); - } - - @Override - public int getEndColumn() { - if (this.endColumn > 0) { - return this.endColumn; - } - Node parent = jjtGetParent(); - if (parent != null) { - return parent.getEndColumn(); - } - throw new RuntimeException("Unable to determine ending column of Node."); - } - - /** - * Accept the visitor. * - */ - @Override - public Object childrenAccept(ApexParserVisitor visitor, Object data) { - if (children != null) { - for (int i = 0; i < children.length; ++i) { - @SuppressWarnings("unchecked") - // we know that the children here are all ApexNodes - ApexNode apexNode = (ApexNode) children[i]; - apexNode.jjtAccept(visitor, data); - } - } - return data; - } - @Override public T getNode() { return node; @@ -132,15 +53,6 @@ public abstract class AbstractApexNode extends AbstractNode i } } - - - - @Override - public final String getXPathNodeName() { - return this.getClass().getSimpleName().replaceFirst("^AST", ""); - } - - public String getLocation() { if (hasRealLoc()) { return String.valueOf(node.getLoc()); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNodeBase.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNodeBase.java new file mode 100644 index 0000000000..05194407b8 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNodeBase.java @@ -0,0 +1,103 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import net.sourceforge.pmd.lang.ast.AbstractNode; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.SourceCodePositioner; + +public abstract class AbstractApexNodeBase extends AbstractNode { + + public AbstractApexNodeBase(Class klass) { + super(klass.hashCode()); + } + + /* package */ void calculateLineNumbers(SourceCodePositioner positioner, int startOffset, int endOffset) { + // end column will be interpreted as inclusive, while endOffset/endIndex + // is exclusive + endOffset -= 1; + + this.beginLine = positioner.lineNumberFromOffset(startOffset); + this.beginColumn = positioner.columnFromOffset(this.beginLine, startOffset); + this.endLine = positioner.lineNumberFromOffset(endOffset); + this.endColumn = positioner.columnFromOffset(this.endLine, endOffset); + + if (this.endColumn < 0) { + this.endColumn = 0; + } + } + + /** + * Accept the visitor. * + */ + abstract Object jjtAccept(ApexParserVisitor visitor, Object data); + + /** + * Accept the visitor. * + */ + public Object childrenAccept(ApexParserVisitor visitor, Object data) { + if (children != null) { + for (int i = 0; i < children.length; ++i) { + // we know that the children here are all ApexNodes + AbstractApexNodeBase apexNode = (AbstractApexNodeBase) children[i]; + apexNode.jjtAccept(visitor, data); + } + } + return data; + } + + @Override + public int getBeginLine() { + if (this.beginLine > 0) { + return this.beginLine; + } + Node parent = jjtGetParent(); + if (parent != null) { + return parent.getBeginLine(); + } + throw new RuntimeException("Unable to determine beginning line of Node."); + } + + @Override + public int getBeginColumn() { + if (this.beginColumn > 0) { + return this.beginColumn; + } + Node parent = jjtGetParent(); + if (parent != null) { + return parent.getBeginColumn(); + } + throw new RuntimeException("Unable to determine beginning column of Node."); + } + + @Override + public int getEndLine() { + if (this.endLine > 0) { + return this.endLine; + } + Node parent = jjtGetParent(); + if (parent != null) { + return parent.getEndLine(); + } + throw new RuntimeException("Unable to determine ending line of Node."); + } + + @Override + public int getEndColumn() { + if (this.endColumn > 0) { + return this.endColumn; + } + Node parent = jjtGetParent(); + if (parent != null) { + return parent.getEndColumn(); + } + throw new RuntimeException("Unable to determine ending column of Node."); + } + + @Override + public final String getXPathNodeName() { + return this.getClass().getSimpleName().replaceFirst("^AST", ""); + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitor.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitor.java index 105794e95a..bc5be4f743 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitor.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitor.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.lang.apex.ast; public interface ApexParserVisitor { + Object visit(AbstractApexNodeBase node, Object data); + Object visit(ApexNode node, Object data); Object visit(ASTAnnotation node, Object data); @@ -67,6 +69,8 @@ public interface ApexParserVisitor { Object visit(ASTFieldDeclarationStatements node, Object data); + Object visit(ASTFormalComment node, Object data); + Object visit(ASTForEachStatement node, Object data); Object visit(ASTForLoopStatement node, Object data); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitorAdapter.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitorAdapter.java index 639b88ec87..40ceea0343 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitorAdapter.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitorAdapter.java @@ -5,6 +5,11 @@ package net.sourceforge.pmd.lang.apex.ast; public class ApexParserVisitorAdapter implements ApexParserVisitor { + @Override + public Object visit(AbstractApexNodeBase node, Object data) { + return node.childrenAccept(this, data); + } + @Override public Object visit(ApexNode node, Object data) { return node.childrenAccept(this, data); @@ -449,4 +454,9 @@ public class ApexParserVisitorAdapter implements ApexParserVisitor { public Object visit(ASTStatementExecuted node, Object data) { return visit((ApexNode) node, data); } + + @Override + public Object visit(ASTFormalComment node, Object data) { + return visit((AbstractApexNodeBase) node, data); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java index 0c66452b04..e55005d186 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java @@ -6,13 +6,20 @@ package net.sourceforge.pmd.lang.apex.ast; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Stack; +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.Token; + import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.SourceCodePositioner; +import apex.jorje.parser.impl.ApexLexer; import apex.jorje.semantic.ast.AstNode; import apex.jorje.semantic.ast.compilation.AnonymousClass; import apex.jorje.semantic.ast.compilation.ConstructorPreamble; @@ -216,17 +223,19 @@ public final class ApexTreeBuilder extends AstVisitor { // The Apex nodes with children to build. private Stack parents = new Stack<>(); + + private AdditionalPassScope scope = new AdditionalPassScope(Errors.createErrors()); private final SourceCodePositioner sourceCodePositioner; private final String sourceCode; + private ListIterator apexDocTokenLocations; public ApexTreeBuilder(String sourceCode) { this.sourceCode = sourceCode; sourceCodePositioner = new SourceCodePositioner(sourceCode); + apexDocTokenLocations = buildApexDocTokenLocations(sourceCode).listIterator(); } - AdditionalPassScope scope = new AdditionalPassScope(Errors.createErrors()); - static AbstractApexNode createNodeAdapter(T node) { try { @SuppressWarnings("unchecked") @@ -271,6 +280,78 @@ public final class ApexTreeBuilder extends AstVisitor { return node; } + private void buildFormalComment(AstNode node) { + if (parents.peek() == node) { + ApexNode parent = (ApexNode) nodes.peek(); + TokenLocation tokenLocation = getApexDocTokenLocation(getApexDocIndex(parent)); + if (tokenLocation != null) { + ASTFormalComment comment = new ASTFormalComment(tokenLocation.token); + comment.calculateLineNumbers(sourceCodePositioner, tokenLocation.index, + tokenLocation.index + tokenLocation.token.length()); + parent.jjtAddChild(comment, 0); + comment.jjtSetParent(parent); + } + } + } + + private int getApexDocIndex(ApexNode node) { + final int index = node.getNode().getLoc().getStartIndex(); + return sourceCode.lastIndexOf('\n', index); + } + + private TokenLocation getApexDocTokenLocation(int index) { + TokenLocation last = null; + while (apexDocTokenLocations.hasNext()) { + final TokenLocation location = apexDocTokenLocations.next(); + if (location.index >= index) { + // rollback, the next token corresponds to a different node + apexDocTokenLocations.previous(); + + if (last != null) { + return last; + } + return null; + } + last = location; + } + return last; + } + + private static List buildApexDocTokenLocations(String source) { + ANTLRStringStream stream = new ANTLRStringStream(source); + ApexLexer lexer = new ApexLexer(stream); + + ArrayList tokenLocations = new ArrayList<>(); + int startIndex = 0; + Token token = lexer.nextToken(); + int endIndex = lexer.getCharIndex(); + while (token.getType() != Token.EOF) { + if (token.getType() == ApexLexer.BLOCK_COMMENT) { + + // Filter only block comments starting with "/**" + if (token.getText().startsWith("/**")) { + tokenLocations.add(new TokenLocation(startIndex, token.getText())); + } + } + // TODO : Check other non-doc comments and tokens of type ApexLexer.EOL_COMMENT for "NOPMD" suppressions + startIndex = endIndex; + token = lexer.nextToken(); + endIndex = lexer.getCharIndex(); + } + + return tokenLocations; + } + + private static class TokenLocation { + int index; + String token; + + TokenLocation(int index, String token) { + this.index = index; + this.token = token; + } + } + private boolean visit(AstNode node) { if (parents.peek() == node) { return true; @@ -287,7 +368,9 @@ public final class ApexTreeBuilder extends AstVisitor { @Override public boolean visit(UserInterface node, AdditionalPassScope scope) { - return visit(node); + final boolean ret = visit(node); + buildFormalComment(node); + return ret; } @Override @@ -572,7 +655,9 @@ public final class ApexTreeBuilder extends AstVisitor { @Override public boolean visit(Property node, AdditionalPassScope scope) { - return visit(node); + final boolean ret = visit(node); + buildFormalComment(node); + return ret; } @Override @@ -637,12 +722,16 @@ public final class ApexTreeBuilder extends AstVisitor { @Override public boolean visit(UserClass node, AdditionalPassScope scope) { - return visit(node); + final boolean ret = visit(node); + buildFormalComment(node); + return ret; } @Override public boolean visit(Method node, AdditionalPassScope scope) { - return visit(node); + final boolean ret = visit(node); + buildFormalComment(node); + return ret; } @Override diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java index 5f371792b6..d78ad6aca5 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java @@ -43,6 +43,7 @@ import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclarationStatements; import net.sourceforge.pmd.lang.apex.ast.ASTForEachStatement; import net.sourceforge.pmd.lang.apex.ast.ASTForLoopStatement; +import net.sourceforge.pmd.lang.apex.ast.ASTFormalComment; import net.sourceforge.pmd.lang.apex.ast.ASTIfBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTIfElseBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTIllegalStoreExpression; @@ -99,6 +100,7 @@ import net.sourceforge.pmd.lang.apex.ast.ASTVariableDeclaration; import net.sourceforge.pmd.lang.apex.ast.ASTVariableDeclarationStatements; import net.sourceforge.pmd.lang.apex.ast.ASTVariableExpression; import net.sourceforge.pmd.lang.apex.ast.ASTWhileLoopStatement; +import net.sourceforge.pmd.lang.apex.ast.AbstractApexNodeBase; import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.apex.ast.ApexParserVisitor; import net.sourceforge.pmd.lang.ast.Node; @@ -138,6 +140,12 @@ public abstract class AbstractApexRule extends AbstractRule } } + @Override + public Object visit(AbstractApexNodeBase node, Object data) { + node.childrenAccept(this, data); + return null; + } + @Override public Object visit(ApexNode node, Object data) { node.childrenAccept(this, data); @@ -583,4 +591,9 @@ public abstract class AbstractApexRule extends AbstractRule public Object visit(ASTStatementExecuted node, Object data) { return visit((ApexNode) node, data); } + + @Override + public Object visit(ASTFormalComment node, Object data) { + return visit((AbstractApexNodeBase) node, data); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/AvoidGlobalModifierRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/AvoidGlobalModifierRule.java index 82416f0697..9d583e7f0c 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/AvoidGlobalModifierRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/AvoidGlobalModifierRule.java @@ -4,14 +4,18 @@ package net.sourceforge.pmd.lang.apex.rule.bestpractices; -import static apex.jorje.semantic.symbol.type.ModifierTypeInfos.GLOBAL; +import java.util.List; +import net.sourceforge.pmd.lang.apex.ast.ASTAnnotation; +import net.sourceforge.pmd.lang.apex.ast.ASTMethod; import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode; import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; import net.sourceforge.pmd.lang.apex.ast.ASTUserInterface; import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; +import apex.jorje.semantic.symbol.type.ModifierTypeInfos; + public class AvoidGlobalModifierRule extends AbstractApexRule { public AvoidGlobalModifierRule() { @@ -33,10 +37,39 @@ public class AvoidGlobalModifierRule extends AbstractApexRule { private Object checkForGlobal(ApexNode node, Object data) { ASTModifierNode modifierNode = node.getFirstChildOfType(ASTModifierNode.class); - if (modifierNode != null && modifierNode.getNode().getModifiers().has(GLOBAL)) { + if (isGlobal(modifierNode) && !hasRestAnnotation(modifierNode) && !hasWebServices(node)) { addViolation(data, node); } return data; } + + private boolean hasWebServices(ApexNode node) { + List methods = node.findChildrenOfType(ASTMethod.class); + for (ASTMethod method : methods) { + ASTModifierNode methodModifier = method.getFirstChildOfType(ASTModifierNode.class); + if (isWebService(methodModifier)) { + return true; + } + } + return false; + } + + private boolean isWebService(ASTModifierNode modifierNode) { + return modifierNode != null && modifierNode.getNode().getModifiers().has(ModifierTypeInfos.WEB_SERVICE); + } + + private boolean isGlobal(ASTModifierNode modifierNode) { + return modifierNode != null && modifierNode.getNode().getModifiers().has(ModifierTypeInfos.GLOBAL); + } + + private boolean hasRestAnnotation(ASTModifierNode modifierNode) { + List annotations = modifierNode.findChildrenOfType(ASTAnnotation.class); + for (ASTAnnotation annotation : annotations) { + if (annotation.hasImageEqualTo("RestResource")) { + return true; + } + } + return false; + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/documentation/ApexDocRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/documentation/ApexDocRule.java new file mode 100644 index 0000000000..d50277fd23 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/documentation/ApexDocRule.java @@ -0,0 +1,180 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.rule.documentation; + +import static apex.jorje.semantic.symbol.type.ModifierTypeInfos.GLOBAL; +import static apex.jorje.semantic.symbol.type.ModifierTypeInfos.OVERRIDE; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import net.sourceforge.pmd.lang.apex.ast.ASTAnnotation; +import net.sourceforge.pmd.lang.apex.ast.ASTFormalComment; +import net.sourceforge.pmd.lang.apex.ast.ASTMethod; +import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode; +import net.sourceforge.pmd.lang.apex.ast.ASTProperty; +import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; +import net.sourceforge.pmd.lang.apex.ast.ASTUserInterface; +import net.sourceforge.pmd.lang.apex.ast.ApexNode; +import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; + +import apex.jorje.data.Locations; +import apex.jorje.semantic.ast.modifier.ModifierGroup; + +public class ApexDocRule extends AbstractApexRule { + private static final Pattern DESCRIPTION_PATTERN = Pattern.compile("@description\\s"); + private static final Pattern RETURN_PATTERN = Pattern.compile("@return\\s"); + private static final Pattern PARAM_PATTERN = Pattern.compile("@param\\s+(\\w+)\\s"); + + private static final String MISSING_COMMENT_MESSAGE = "Missing ApexDoc comment"; + private static final String MISSING_DESCRIPTION_MESSAGE = "Missing ApexDoc @description"; + private static final String MISSING_RETURN_MESSAGE = "Missing ApexDoc @return"; + private static final String UNEXPECTED_RETURN_MESSAGE = "Unexpected ApexDoc @return"; + private static final String MISMATCHED_PARAM_MESSAGE = "Missing or mismatched ApexDoc @param"; + + public ApexDocRule() { + addRuleChainVisit(ASTUserClass.class); + addRuleChainVisit(ASTUserInterface.class); + addRuleChainVisit(ASTMethod.class); + addRuleChainVisit(ASTProperty.class); + } + + @Override + public Object visit(ASTUserClass node, Object data) { + handleClassOrInterface(node, data); + return data; + } + + @Override + public Object visit(ASTUserInterface node, Object data) { + handleClassOrInterface(node, data); + return data; + } + + @Override + public Object visit(ASTMethod node, Object data) { + if (node.jjtGetParent() instanceof ASTProperty) { + // Skip property methods, doc is required on the property itself + return data; + } + + ApexDocComment comment = getApexDocComment(node); + if (comment == null) { + if (shouldHaveApexDocs(node)) { + addViolationWithMessage(data, node, MISSING_COMMENT_MESSAGE); + } + } else { + if (!comment.hasDescription) { + addViolationWithMessage(data, node, MISSING_DESCRIPTION_MESSAGE); + } + + String returnType = node.getNode().getReturnTypeRef().toString(); + boolean shouldHaveReturn = !(returnType.isEmpty() || "void".equalsIgnoreCase(returnType)); + if (comment.hasReturn != shouldHaveReturn) { + if (shouldHaveReturn) { + addViolationWithMessage(data, node, MISSING_RETURN_MESSAGE); + } else { + addViolationWithMessage(data, node, UNEXPECTED_RETURN_MESSAGE); + } + } + + // Collect parameter names in order + final List params = node.getNode().getMethodInfo().getParameters() + .stream().map(p -> p.getName().getValue()).collect(Collectors.toList()); + + if (!comment.params.equals(params)) { + addViolationWithMessage(data, node, MISMATCHED_PARAM_MESSAGE); + } + } + + return data; + } + + @Override + public Object visit(ASTProperty node, Object data) { + ApexDocComment comment = getApexDocComment(node); + if (comment == null) { + if (shouldHaveApexDocs(node)) { + addViolationWithMessage(data, node, MISSING_COMMENT_MESSAGE); + } + } else { + if (!comment.hasDescription) { + addViolationWithMessage(data, node, MISSING_DESCRIPTION_MESSAGE); + } + } + + return data; + } + + private void handleClassOrInterface(ApexNode node, Object data) { + ApexDocComment comment = getApexDocComment(node); + if (comment == null) { + if (shouldHaveApexDocs(node)) { + addViolationWithMessage(data, node, MISSING_COMMENT_MESSAGE); + } + } else { + if (!comment.hasDescription) { + addViolationWithMessage(data, node, MISSING_DESCRIPTION_MESSAGE); + } + } + } + + private boolean shouldHaveApexDocs(ApexNode node) { + if (node.getNode().getLoc() == Locations.NONE) { + return false; + } + + // is this a test? + for (final ASTAnnotation annotation : node.findDescendantsOfType(ASTAnnotation.class)) { + if (annotation.getImage().equals("IsTest")) { + return false; + } + } + + ASTModifierNode modifier = node.getFirstChildOfType(ASTModifierNode.class); + if (modifier != null) { + boolean isPublic = modifier.isPublic(); + ModifierGroup modifierGroup = modifier.getNode().getModifiers(); + boolean isGlobal = modifierGroup.has(GLOBAL); + boolean isOverride = modifierGroup.has(OVERRIDE); + return (isPublic || isGlobal) && !isOverride; + } + return false; + } + + private ApexDocComment getApexDocComment(ApexNode node) { + ASTFormalComment comment = node.getFirstChildOfType(ASTFormalComment.class); + if (comment != null) { + String token = comment.getToken(); + + boolean hasDescription = DESCRIPTION_PATTERN.matcher(token).find(); + boolean hasReturn = RETURN_PATTERN.matcher(token).find(); + + ArrayList params = new ArrayList<>(); + Matcher paramMatcher = PARAM_PATTERN.matcher(token); + while (paramMatcher.find()) { + params.add(paramMatcher.group(1)); + } + + return new ApexDocComment(hasDescription, hasReturn, params); + } + return null; + } + + private static class ApexDocComment { + boolean hasDescription; + boolean hasReturn; + List params; + + ApexDocComment(boolean hasDescription, boolean hasReturn, List params) { + this.hasDescription = hasDescription; + this.hasReturn = hasReturn; + this.params = params; + } + } +} diff --git a/pmd-apex/src/main/resources/category/apex/documentation.xml b/pmd-apex/src/main/resources/category/apex/documentation.xml index 1e5fd3b0bf..30e5276410 100644 --- a/pmd-apex/src/main/resources/category/apex/documentation.xml +++ b/pmd-apex/src/main/resources/category/apex/documentation.xml @@ -8,4 +8,40 @@ Rules that are related to code documentation. + + + +This rule validates that: + +* ApexDoc comments are present for classes, methods, and properties that are public or global, excluding +overrides and test classes (as well as the contents of test classes). +* ApexDoc comments should contain @description. +* ApexDoc comments on non-void, non-constructor methods should contain @return. +* ApexDoc comments on void or constructor methods should not contain @return. +* ApexDoc comments on methods with parameters should contain @param for each parameter, in the same +order as the method signature. + +Method overrides and tests are both exempted from having ApexDoc. + + 3 + + + + + diff --git a/pmd-apex/src/main/resources/rulesets/apex/ruleset.xml b/pmd-apex/src/main/resources/rulesets/apex/ruleset.xml index f414d496a0..4c802e477a 100644 --- a/pmd-apex/src/main/resources/rulesets/apex/ruleset.xml +++ b/pmd-apex/src/main/resources/rulesets/apex/ruleset.xml @@ -430,4 +430,15 @@ + + + + 3 + + + + + + + diff --git a/pmd-apex/src/main/resources/rulesets/apex/rulesets.properties b/pmd-apex/src/main/resources/rulesets/apex/rulesets.properties index 4c45115d8f..dab0624aee 100644 --- a/pmd-apex/src/main/resources/rulesets/apex/rulesets.properties +++ b/pmd-apex/src/main/resources/rulesets/apex/rulesets.properties @@ -6,6 +6,7 @@ rulesets.filenames=\ category/apex/bestpractices.xml,\ category/apex/codestyle.xml,\ category/apex/design.xml,\ + category/apex/documentation.xml,\ category/apex/errorprone.xml,\ category/apex/performance.xml,\ category/apex/security.xml @@ -13,5 +14,4 @@ rulesets.filenames=\ # # categories with no rules yet # -#category/apex/documentation.xml #category/apex/multithreading.xml diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/documentation/DocumentationRulesTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/documentation/DocumentationRulesTest.java new file mode 100644 index 0000000000..2198b0a7ed --- /dev/null +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/documentation/DocumentationRulesTest.java @@ -0,0 +1,17 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.rule.documentation; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +public class DocumentationRulesTest extends SimpleAggregatorTst { + + private static final String RULESET = "category/apex/documentation.xml"; + + @Override + public void setUp() { + addRule(RULESET, "ApexDoc"); + } +} diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/AvoidGlobalModifier.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/AvoidGlobalModifier.xml index ead264bc97..28f5acf78c 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/AvoidGlobalModifier.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/AvoidGlobalModifier.xml @@ -48,5 +48,44 @@ global class Foo { } ]]> - + + + #1348 [apex] AvoidGlobalModifierRule gives warning even when its a REST webservice - false positive + 0 + + + + + #1348 [apex] AvoidGlobalModifierRule gives warning even when its a SOAP webservice - false positive + 0 + + + + + Simple public and non-global class + 0 + + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml new file mode 100644 index 0000000000..0e890cd274 --- /dev/null +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml @@ -0,0 +1,401 @@ + + + + + + comment should start with two asterisks + 1 + + + + + public test class does not need comment + 0 + + + + + public class should have comment + 1 + + + + + global class should have comment + 1 + + + + + private class does not need comment + 0 + + + + + class comment should have description + 1 + + + + + correct class comment + 0 + + + + + correct class comment with annotation + 0 + + + + + public interface should have comment + 1 + + + + + global interface should have comment + 1 + + + + + private interface does not need comment + 0 + + + + + interface comment should have description + 1 + + + + + correct interface comment + 0 + + + + + correct interface comment with annotation + 0 + + + + + public method should have comment + 1 + + + + + global method should have comment + 1 + + + + + public override method does not need comment + 0 + + + + + method comment should have description + 1 + + + + + non-void method comment should have return + 1 + + + + + constructor comment does not need return + 0 + + + + + method with param comment should have param + 1 + + + + + params should match method + 1 + + + + + correct void method comment + 0 + + + + + correct non-void method comment + 0 + + + + + correct method with params comment + 0 + + + + + correct method comment with annotation + 0 + + + + + public property should have comment + 1 + + + + + global property should have comment + 1 + + + + + property comment should have description + 1 + + + + + correct property comment + 0 + + + + + correct property comment with annotation + 0 + + + + diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index 19059bc88a..bf43019fe7 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT @@ -96,7 +96,10 @@ ant provided - + + org.antlr + antlr4-runtime + com.beust jcommander diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java index f0a2760a2c..3fc13e727c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java @@ -112,6 +112,7 @@ public interface Rule extends PropertySource { * * @return the name */ + @Override String getName(); /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java index bbab5ec797..71d5bc24a5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java @@ -58,12 +58,9 @@ public class RuleSet implements ChecksumAware { /** * Creates a new RuleSet with the given checksum. - * - * @param checksum - * A checksum of the ruleset, should change only if the ruleset - * was configured differently - * @param rules - * The rules to be applied as part of this ruleset + * + * @param builder + * A rule set builder. */ private RuleSet(final RuleSetBuilder builder) { checksum = builder.checksum; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/Experimental.java b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/Experimental.java new file mode 100644 index 0000000000..82a838e616 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/Experimental.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.annotation; + +import java.lang.annotation.Documented; + + +/** + * Indicates the feature is in experimental state: its existence, signature or behavior + * might change without warning from one release to the next. + * + * @since 6.7.0 + */ +@Documented +public @interface Experimental { +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/InternalApi.java b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/InternalApi.java new file mode 100644 index 0000000000..fbcef90350 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/InternalApi.java @@ -0,0 +1,25 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.annotation; + +import java.lang.annotation.Documented; + + +/** + * Tags API members that are not publicly supported API. + * Such members may be removed, renamed, moved, or otherwise + * broken at any time and should not be relied upon outside + * of the main PMD codebase. + * + *

Members and types tagged with this annotation will remain + * supported until 7.0.0, after which some will be moved to internal + * packages, or will see their visibility reduced. + * + * @since 6.7.0 + */ +// NOTE: use @Deprecated with this annotation to raise a compiler warning until 7.0.0 +@Documented +public @interface InternalApi { +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java new file mode 100644 index 0000000000..47f10de80c --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/annotation/ReservedSubclassing.java @@ -0,0 +1,37 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + + +/** + * Indicates that subclassing this type is not publicly + * supported API. Abstract methods may be added or removed + * at any time, which could break binary compatibility with + * existing implementors. Protected methods are also part of + * the private API of this type. + * + *

The API that is not inheritance-specific (unless {@linkplain InternalApi noted otherwise}, + * all public members), is still public API and will remain binary- + * compatible between major releases. + * + *

Types tagged with this annotation will remain supported + * until 7.0.0, at which point no guarantees will be maintained + * about the stability of the inheritance hierarchy for external + * clients. + * + *

This should be used for example for base rule classes that + * are meant to be used in PMD only, or for AST-related interfaces + * and abstract classes. + * + * @since 6.7.0 + */ +@Target(ElementType.TYPE) +@Documented +public @interface ReservedSubclassing { +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java index fc45da1b19..03fe697a36 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDCommandLineInterface.java @@ -102,25 +102,19 @@ public final class PMDCommandLineInterface { final String launchCmd = getWindowsLaunchCmd(); final String WINDOWS_PATH_TO_CODE = "c:\\my\\source\\code "; - return "For example on windows: " + PMD.EOL + launchCmd + " -dir " + WINDOWS_PATH_TO_CODE - + "-format text -R java-unusedcode,java-imports -version 1.5 -language java -debug" + PMD.EOL - + launchCmd + " -dir " + WINDOWS_PATH_TO_CODE - + "-f xml -rulesets java-basic,java-design -encoding UTF-8" + PMD.EOL + launchCmd + " -d " - + WINDOWS_PATH_TO_CODE + "-rulesets java-typeresolution -auxclasspath commons-collections.jar;derby.jar" - + PMD.EOL + launchCmd + " -d " + WINDOWS_PATH_TO_CODE - + "-f html -R java-typeresolution -auxclasspath file:///C:/my/classpathfile" + PMD.EOL + PMD.EOL; + return "For example on windows: " + PMD.EOL + + launchCmd + " -dir " + WINDOWS_PATH_TO_CODE + "-format text -R rulesets/java/quickstart.xml -version 1.5 -language java -debug" + PMD.EOL + + launchCmd + " -dir " + WINDOWS_PATH_TO_CODE + "-f xml -rulesets rulesets/java/quickstart.xml,category/java/codestyle.xml -encoding UTF-8" + PMD.EOL + + launchCmd + " -d " + WINDOWS_PATH_TO_CODE + "-rulesets rulesets/java/quickstart.xml -auxclasspath lib\\commons-collections.jar;lib\\derby.jar" + PMD.EOL + + launchCmd + " -d " + WINDOWS_PATH_TO_CODE + "-f html -R rulesets/java/quickstart.xml -auxclasspath file:///C:/my/classpathfile" + PMD.EOL + PMD.EOL; } private static String getUnixExample() { - final String UNIX_PROMPT = "$ "; - final String launchCmd = "pmd-bin-" + PMDVersion.VERSION + "/bin/run.sh pmd"; - return "For example on *nix: " + PMD.EOL + UNIX_PROMPT + launchCmd - + " -dir /home/workspace/src/main/java/code -f html -rulesets java-basic,java-design" + PMD.EOL - + UNIX_PROMPT + launchCmd - + " -d ./src/main/java/code -f xslt -R java-basic,java-design -property xsltFilename=my-own.xsl" - + PMD.EOL + UNIX_PROMPT + launchCmd - + " -d ./src/main/java/code -f html -R java-typeresolution -auxclasspath commons-collections.jar:derby.jar" - + PMD.EOL; + final String launchCmd = "$ pmd-bin-" + PMDVersion.VERSION + "/bin/run.sh pmd"; + return "For example on *nix: " + PMD.EOL + + launchCmd + " -dir /home/workspace/src/main/java/code -f html -rulesets rulesets/java/quickstart.xml,category/java/codestyle.xml" + PMD.EOL + + launchCmd + " -d ./src/main/java/code -R rulesets/java/quickstart.xml -f xslt -property xsltFilename=my-own.xsl" + PMD.EOL + + launchCmd + " -d ./src/main/java/code -f html -R rulesets/java/quickstart.xml -auxclasspath commons-collections.jar:derby.jar" + PMD.EOL; } private static String supportedVersions() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AntlrTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AntlrTokenizer.java new file mode 100644 index 0000000000..a5082f5037 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AntlrTokenizer.java @@ -0,0 +1,54 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Token; + +import net.sourceforge.pmd.lang.antlr.AntlrTokenManager; +import net.sourceforge.pmd.lang.ast.TokenMgrError; + +/** + * Generic implementation of a {@link Tokenizer} useful to any Antlr grammar. + */ +public abstract class AntlrTokenizer implements Tokenizer { + + protected abstract AntlrTokenManager getLexerForSource(SourceCode sourceCode); + + @Override + public void tokenize(final SourceCode sourceCode, final Tokens tokenEntries) { + + AntlrTokenManager tokenManager = getLexerForSource(sourceCode); + + try { + Token token = (Token) tokenManager.getNextToken(); + + while (token.getType() != Token.EOF) { + if (token.getChannel() != Lexer.HIDDEN) { + final TokenEntry tokenEntry = + new TokenEntry(token.getText(), tokenManager.getFileName(), token.getLine()); + + tokenEntries.add(tokenEntry); + } + token = (Token) tokenManager.getNextToken(); + } + } catch (final AntlrTokenManager.ANTLRSyntaxError err) { + // Wrap exceptions of the ANTLR tokenizer in a TokenMgrError, so they are correctly handled + // when CPD is executed with the '--skipLexicalErrors' command line option + throw new TokenMgrError("Lexical error in file " + tokenManager.getFileName() + " at line " + + err.getLine() + ", column " + err.getColumn() + ". Encountered: " + err.getMessage(), + TokenMgrError.LEXICAL_ERROR); + } finally { + tokenEntries.add(TokenEntry.getEOF()); + } + } + + /* default */ static CharStream getCharStreamFromSourceCode(final SourceCode sourceCode) { + StringBuilder buffer = sourceCode.getCodeBuffer(); + return CharStreams.fromString(buffer.toString()); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/antlr/AntlrTokenManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/antlr/AntlrTokenManager.java new file mode 100644 index 0000000000..8b0e4b0712 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/antlr/AntlrTokenManager.java @@ -0,0 +1,82 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.antlr; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +import net.sourceforge.pmd.lang.TokenManager; + +/** + * Generic token manager implementation for all Antlr lexers. + */ +public class AntlrTokenManager implements TokenManager { + private final Lexer lexer; + private String fileName; + + /** + * Constructor + * + * @param lexer The lexer + * @param fileName The file name + */ + public AntlrTokenManager(final Lexer lexer, final String fileName) { + this.lexer = lexer; + this.fileName = fileName; + resetListeners(); + } + + @Override + public Object getNextToken() { + return lexer.nextToken(); + } + + @Override + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getFileName() { + return fileName; + } + + private void resetListeners() { + lexer.removeErrorListeners(); + lexer.addErrorListener(new ErrorHandler()); + } + + + private static class ErrorHandler extends BaseErrorListener { + + @Override + public void syntaxError(final Recognizer recognizer, final Object offendingSymbol, final int line, + final int charPositionInLine, final String msg, final RecognitionException ex) { + throw new ANTLRSyntaxError(msg, line, charPositionInLine, ex); + } + } + + public static class ANTLRSyntaxError extends RuntimeException { + private static final long serialVersionUID = 1L; + private final int line; + private final int column; + + /* default */ ANTLRSyntaxError(final String msg, final int line, final int column, + final RecognitionException cause) { + super(msg, cause); + this.line = line; + this.column = column; + } + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java index f607d03aa5..062f3756d4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java @@ -21,7 +21,11 @@ import net.sourceforge.pmd.properties.PropertySource; /** * Base class for Rule implementations which delegate to another Rule instance. + * + * @deprecated This is only relevant to {@link RuleReference}, but prevents sharing the implementation + * of {@link net.sourceforge.pmd.properties.AbstractPropertySource}. Will be removed in 7.0.0 */ +@Deprecated public abstract class AbstractDelegateRule implements Rule { private Rule rule; @@ -30,6 +34,13 @@ public abstract class AbstractDelegateRule implements Rule { return rule; } + + /** + * @deprecated This will be removed in 7.0.0 + * I mark it specially deprecated because it's inherited by rule reference, + * even though a RuleReference has no business setting its rule after construction + */ + @Deprecated public void setRule(Rule rule) { this.rule = rule; } @@ -213,6 +224,10 @@ public abstract class AbstractDelegateRule implements Rule { rule.setProperty(propertyDescriptor, values); } + @Override + public boolean isPropertyOverridden(PropertyDescriptor propertyDescriptor) { + return rule.isPropertyOverridden(propertyDescriptor); + } @Override public Map, Object> getPropertiesByPropertyDescriptor() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java index eaf20ab4d1..c2614de710 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java @@ -48,6 +48,11 @@ public abstract class AbstractRule extends AbstractPropertySource implements Rul definePropertyDescriptor(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR); } + @Override + protected String getPropertySourceType() { + return "rule"; + } + /** * @deprecated Use {@link #deepCopy()} to create verbatim copies of rules. */ @@ -66,7 +71,7 @@ public abstract class AbstractRule extends AbstractPropertySource implements Rul otherRule.examples = copyExamples(); otherRule.externalInfoUrl = externalInfoUrl; otherRule.priority = priority; - otherRule.propertyDescriptors = copyPropertyDescriptors(); + otherRule.propertyDescriptors = new ArrayList<>(getPropertyDescriptors()); otherRule.propertyValuesByDescriptor = copyPropertyValues(); otherRule.usesDFA = usesDFA; otherRule.usesTypeResolution = usesTypeResolution; @@ -459,6 +464,7 @@ public abstract class AbstractRule extends AbstractPropertySource implements Rul } rule.setPriority(getPriority()); for (final PropertyDescriptor prop : getPropertyDescriptors()) { + // define the descriptor only if it doesn't yet exist if (rule.getPropertyDescriptor(prop.name()) == null) { rule.definePropertyDescriptor(prop); // Property descriptors are immutable, and can be freely shared } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java index a8b8aaa959..15b2609f41 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java @@ -5,12 +5,12 @@ package net.sourceforge.pmd.lang.rule; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; @@ -43,17 +43,19 @@ public class RuleReference extends AbstractDelegateRule { private RulePriority priority; private RuleSetReference ruleSetReference; - private static final List> EMPTY_DESCRIPTORS = Collections.emptyList(); - @Deprecated // to be removed with PMD 7.0.0 - // when creating a rule reference, always provide the rule and the ruleset, see - // the constructor RuleReference(Rule, RuleSetReference) + /** + * @deprecated to be removed with PMD 7.0.0. when creating a rule reference, always + * provide the rule and the ruleset, see the constructor RuleReference(Rule, RuleSetReference) + */ + @Deprecated public RuleReference() { // default constructor } /** - * + * Create a new reference to the given rule. + * * @param theRule the referenced rule * @param theRuleSetReference the rule set, where the rule is defined */ @@ -62,15 +64,38 @@ public class RuleReference extends AbstractDelegateRule { ruleSetReference = theRuleSetReference; } + + /** copy constructor */ + private RuleReference(RuleReference ref) { + + this.language = ref.language; + this.minimumLanguageVersion = ref.minimumLanguageVersion; + this.maximumLanguageVersion = ref.maximumLanguageVersion; + this.deprecated = ref.deprecated; + this.name = ref.name; + this.propertyDescriptors = ref.propertyDescriptors; + this.propertyValues = ref.propertyValues == null ? null : new HashMap<>(ref.propertyValues); + this.message = ref.message; + this.description = ref.description; + this.examples = ref.examples == null ? null : new ArrayList<>(ref.examples); + this.externalInfoUrl = ref.externalInfoUrl; + this.priority = ref.priority; + this.ruleSetReference = ref.ruleSetReference; + + this.setRule(ref.getRule().deepCopy()); + } + public Language getOverriddenLanguage() { return language; } + // FIXME should we really allow overriding the language of a rule? + // I don't see any case where this wouldn't just make the rule fail during execution @Override public void setLanguage(Language language) { // Only override if different than current value, or if already // overridden. - if (!isSame(language, super.getLanguage()) || this.language != null) { + if (!Objects.equals(language, super.getLanguage()) || this.language != null) { this.language = language; super.setLanguage(language); } @@ -84,7 +109,7 @@ public class RuleReference extends AbstractDelegateRule { public void setMinimumLanguageVersion(LanguageVersion minimumLanguageVersion) { // Only override if different than current value, or if already // overridden. - if (!isSame(minimumLanguageVersion, super.getMinimumLanguageVersion()) || this.minimumLanguageVersion != null) { + if (!Objects.equals(minimumLanguageVersion, super.getMinimumLanguageVersion()) || this.minimumLanguageVersion != null) { this.minimumLanguageVersion = minimumLanguageVersion; super.setMinimumLanguageVersion(minimumLanguageVersion); } @@ -98,7 +123,7 @@ public class RuleReference extends AbstractDelegateRule { public void setMaximumLanguageVersion(LanguageVersion maximumLanguageVersion) { // Only override if different than current value, or if already // overridden. - if (!isSame(maximumLanguageVersion, super.getMaximumLanguageVersion()) || this.maximumLanguageVersion != null) { + if (!Objects.equals(maximumLanguageVersion, super.getMaximumLanguageVersion()) || this.maximumLanguageVersion != null) { this.maximumLanguageVersion = maximumLanguageVersion; super.setMaximumLanguageVersion(maximumLanguageVersion); } @@ -110,7 +135,7 @@ public class RuleReference extends AbstractDelegateRule { @Override public boolean isDeprecated() { - return deprecated != null && deprecated.booleanValue(); + return deprecated != null && deprecated; } @Override @@ -179,6 +204,10 @@ public class RuleReference extends AbstractDelegateRule { @Override public void addExample(String example) { + // TODO Intuitively, if some examples are overridden (even with empty value), then + // I think we should discard the previous ones. If the rule needs new examples, + // then the previous ones are not relevant. + // TODO Meaningful override of examples is hard, because they are merely // a list of strings. How does one indicate override of a particular // value? Via index? Rule.setExample(int, String)? But the XML format @@ -229,9 +258,11 @@ public class RuleReference extends AbstractDelegateRule { } } - public List> getOverriddenPropertyDescriptors() { - return propertyDescriptors == null ? EMPTY_DESCRIPTORS : propertyDescriptors; + @Override + public List> getOverriddenPropertyDescriptors() { + return propertyDescriptors == null ? Collections.>emptyList() + : new ArrayList<>(propertyDescriptors); } @Override @@ -246,14 +277,16 @@ public class RuleReference extends AbstractDelegateRule { propertyDescriptors.add(propertyDescriptor); } + + @Override public Map, Object> getOverriddenPropertiesByPropertyDescriptor() { - return propertyValues; + return propertyValues == null ? new HashMap, Object>() : new HashMap<>(propertyValues); } @Override public void setProperty(PropertyDescriptor propertyDescriptor, T value) { // Only override if different than current value. - if (!isSame(super.getProperty(propertyDescriptor), value)) { + if (!Objects.equals(super.getProperty(propertyDescriptor), value)) { if (propertyValues == null) { propertyValues = new HashMap<>(); } @@ -263,14 +296,15 @@ public class RuleReference extends AbstractDelegateRule { } - - - - public RuleSetReference getRuleSetReference() { return ruleSetReference; } + + /** + * @deprecated There's no use in setting the ruleset reference after construction + */ + @Deprecated public void setRuleSetReference(RuleSetReference ruleSetReference) { this.ruleSetReference = ruleSetReference; } @@ -279,19 +313,6 @@ public class RuleReference extends AbstractDelegateRule { return StringUtil.isSame(s1, s2, true, false, true); } - @SuppressWarnings("PMD.CompareObjectsWithEquals") - private static boolean isSame(Object o1, Object o2) { - if (o1 instanceof Object[] && o2 instanceof Object[]) { - return isSame((Object[]) o1, (Object[]) o2); - } - return o1 == o2 || o1 != null && o2 != null && o1.equals(o2); - } - - @SuppressWarnings("PMD.UnusedNullCheckInEquals") - // TODO: fix UnusedNullCheckInEquals rule for Arrays - private static boolean isSame(Object[] a1, Object[] a2) { - return a1 == a2 || a1 != null && a2 != null && Arrays.equals(a1, a2); - } private static boolean contains(Collection collection, String s1) { for (String s2 : collection) { @@ -308,11 +329,21 @@ public class RuleReference extends AbstractDelegateRule { || super.hasDescriptor(descriptor); } + /** + * @deprecated Use {@link #isPropertyOverridden(PropertyDescriptor)} instead + */ + @Deprecated public boolean hasOverriddenProperty(PropertyDescriptor descriptor) { + return isPropertyOverridden(descriptor); + } + + @Override + public boolean isPropertyOverridden(PropertyDescriptor descriptor) { return propertyValues != null && propertyValues.containsKey(descriptor); } @Override + @Deprecated public boolean usesDefaultValues() { List> descriptors = getOverriddenPropertyDescriptors(); @@ -321,7 +352,7 @@ public class RuleReference extends AbstractDelegateRule { } for (PropertyDescriptor desc : descriptors) { - if (!isSame(desc.defaultValue(), getProperty(desc))) { + if (!Objects.equals(desc.defaultValue(), getProperty(desc))) { return false; } } @@ -330,6 +361,7 @@ public class RuleReference extends AbstractDelegateRule { } @Override + @Deprecated public void useDefaultValueFor(PropertyDescriptor desc) { // not sure if we should go all the way through to the real thing? @@ -348,29 +380,6 @@ public class RuleReference extends AbstractDelegateRule { @Override public Rule deepCopy() { - RuleReference rule = null; - try { - rule = getClass().newInstance(); - } catch (InstantiationException | IllegalAccessException ignored) { - // Can't happen... we already have an instance - throw new RuntimeException(ignored); // in case it happens anyway, then something is really wrong... - } - rule.setRule(this.getRule().deepCopy()); - - rule.language = language; - rule.minimumLanguageVersion = minimumLanguageVersion; - rule.maximumLanguageVersion = maximumLanguageVersion; - rule.deprecated = deprecated; - rule.name = name; - rule.propertyDescriptors = propertyDescriptors; - rule.propertyValues = propertyValues == null ? null : new HashMap<>(propertyValues); - rule.message = message; - rule.description = description; - rule.examples = examples == null ? null : new ArrayList<>(examples); - rule.externalInfoUrl = externalInfoUrl; - rule.priority = priority; - rule.ruleSetReference = ruleSetReference; - - return rule; + return new RuleReference(this); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java index f465861fb8..2212750a7d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java @@ -17,15 +17,43 @@ import net.sourceforge.pmd.util.CollectionUtil; /** - * Base class for objects which can be configured through properties. Rules and Reports are such objects. + * Base class for {@link PropertySource}. * * @author Brian Remedios */ public abstract class AbstractPropertySource implements PropertySource { - /** The list of known properties that can be configured. */ + // setProperty should probably be hidden from Rule implementations, they have no business using that + // The apex rules that do that could be refactored to do it in the XML + + // TODO RuleReference should extend this class + // This would avoid duplicating the implementation between Rule and RuleReference, + // which should use exactly the same mechanism to override properties (XML). + + // BUT to do that RuleReference should not extend AbstractDelegateRule + // Indeed, AbstractDelegateRule has no business having this overriding logic built-in, + // it would break its contract. For all deeds and purposes that class should be removed. + + // TODO 7.0.0 these fields should be made private final + + /** + * The list of known properties that can be configured. + * + * @deprecated Will be made private final + */ + @Deprecated protected List> propertyDescriptors = new ArrayList<>(); - /** The values for each property. */ + + /** + * The values for each property that were overridden here. + * Default property values are not contained in this map. + * In other words, if this map doesn't contain a descriptor + * which is in {@link #propertyDescriptors}, then it's assumed + * to have a default value. + * + * @deprecated Will be made private final + */ + @Deprecated protected Map, Object> propertyValuesByDescriptor = new HashMap<>(); @@ -33,7 +61,9 @@ public abstract class AbstractPropertySource implements PropertySource { * Creates a copied list of the property descriptors and returns it. * * @return a copy of the property descriptors. + * @deprecated Just use {@link #getPropertyDescriptors()} */ + @Deprecated protected List> copyPropertyDescriptors() { return new ArrayList<>(propertyDescriptors); } @@ -43,13 +73,16 @@ public abstract class AbstractPropertySource implements PropertySource { * Creates a copied map of the values of the properties and returns it. * * @return a copy of the values + * + * @deprecated Just use {@link #getPropertiesByPropertyDescriptor()} or {@link #getOverriddenPropertiesByPropertyDescriptor()} */ + @Deprecated protected Map, Object> copyPropertyValues() { return new HashMap<>(propertyValuesByDescriptor); } - @Override + @Deprecated public Set> ignoredProperties() { return Collections.emptySet(); } @@ -58,11 +91,10 @@ public abstract class AbstractPropertySource implements PropertySource { @Override public void definePropertyDescriptor(PropertyDescriptor propertyDescriptor) { // Check to ensure the property does not already exist. - for (PropertyDescriptor descriptor : propertyDescriptors) { - if (descriptor.name().equals(propertyDescriptor.name())) { - throw new IllegalArgumentException("There is already a PropertyDescriptor with name '" - + propertyDescriptor.name() + "' defined on Rule " + getName() + "."); - } + if (getPropertyDescriptor(propertyDescriptor.name()) != null) { + throw new IllegalArgumentException("There is already a PropertyDescriptor with name '" + + propertyDescriptor.name() + "' defined on " + getPropertySourceType() + " " + getName() + "."); + } propertyDescriptors.add(propertyDescriptor); // Sort in UI order @@ -70,13 +102,7 @@ public abstract class AbstractPropertySource implements PropertySource { } - /** - * Gets the name of the property source. This is e.g. the rule name or the report name. - * - * @return the name - */ - public abstract String getName(); - + protected abstract String getPropertySourceType(); @Override public PropertyDescriptor getPropertyDescriptor(String name) { @@ -91,18 +117,19 @@ public abstract class AbstractPropertySource implements PropertySource { @Override public boolean hasDescriptor(PropertyDescriptor descriptor) { + return propertyDescriptors.contains(descriptor); + } - if (propertyValuesByDescriptor.isEmpty()) { - propertyValuesByDescriptor = getPropertiesByPropertyDescriptor(); - } - return propertyValuesByDescriptor.containsKey(descriptor); + @Override + public final List> getOverriddenPropertyDescriptors() { + return new ArrayList<>(propertyValuesByDescriptor.keySet()); } @Override public List> getPropertyDescriptors() { - return propertyDescriptors; + return Collections.unmodifiableList(propertyDescriptors); } @@ -119,12 +146,17 @@ public abstract class AbstractPropertySource implements PropertySource { } + @Override + public boolean isPropertyOverridden(PropertyDescriptor propertyDescriptor) { + return propertyValuesByDescriptor.containsKey(propertyDescriptor); + } + + @Override public void setProperty(PropertyDescriptor propertyDescriptor, T value) { checkValidPropertyDescriptor(propertyDescriptor); if (value instanceof List) { propertyValuesByDescriptor.put(propertyDescriptor, Collections.unmodifiableList((List) value)); - } else { propertyValuesByDescriptor.put(propertyDescriptor, value); } @@ -144,13 +176,18 @@ public abstract class AbstractPropertySource implements PropertySource { * @param propertyDescriptor The property descriptor to check */ private void checkValidPropertyDescriptor(PropertyDescriptor propertyDescriptor) { - if (!propertyDescriptors.contains(propertyDescriptor)) { - throw new IllegalArgumentException( - "Property descriptor not defined for Rule " + getName() + ": " + propertyDescriptor); + if (!hasDescriptor(propertyDescriptor)) { + throw new IllegalArgumentException("Property descriptor not defined for " + getPropertySourceType() + " " + getName() + ": " + propertyDescriptor); } } + @Override + public final Map, Object> getOverriddenPropertiesByPropertyDescriptor() { + return new HashMap<>(propertyValuesByDescriptor); + } + + @Override public Map, Object> getPropertiesByPropertyDescriptor() { if (propertyDescriptors.isEmpty()) { @@ -168,11 +205,12 @@ public abstract class AbstractPropertySource implements PropertySource { } } - return propertiesByPropertyDescriptor; + return Collections.unmodifiableMap(propertiesByPropertyDescriptor); } @Override + @Deprecated public boolean usesDefaultValues() { Map, Object> valuesByProperty = getPropertiesByPropertyDescriptor(); @@ -191,11 +229,13 @@ public abstract class AbstractPropertySource implements PropertySource { @Override + @Deprecated public void useDefaultValueFor(PropertyDescriptor desc) { propertyValuesByDescriptor.remove(desc); } + // todo Java 8 move up to interface @Override public String dysfunctionReason() { return null; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java index e235e0d74c..7b24644164 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java @@ -10,14 +10,24 @@ import java.util.Set; /** - * Any entity that manages a list of properties is a {@link PropertySource}. These are e.g. Rules and Renderers. + * Entity that manages a list of properties. Properties are described by + * {@linkplain PropertyDescriptor property descriptors}. A property source + * maintains a mapping of property descriptors to property values. The value + * of a property can be set by {@link #setProperty(PropertyDescriptor, Object)}. + * If the property wasn't set with this method, then the property is assumed + * to take a default value, which is specified by {@linkplain PropertyDescriptor#defaultValue() the property descriptor}. + * + *

Bad configuration of the properties may be reported by {@link #dysfunctionReason()}. + * + *

Notable instances of this interface are {@linkplain net.sourceforge.pmd.Rule rules} and + * {@linkplain net.sourceforge.pmd.renderers.Renderer renderers}. * * @author Brian Remedios */ public interface PropertySource { /** - * Define a new property via a PropertyDescriptor. + * Defines a new property. Properties must be defined before they can be set a value. * * @param propertyDescriptor The property descriptor. * @@ -26,6 +36,15 @@ public interface PropertySource { void definePropertyDescriptor(PropertyDescriptor propertyDescriptor) throws IllegalArgumentException; + /** + * Gets the name of this property source. This is e.g. the name + * of the rule or renderer. + * + * @return The name + */ + String getName(); + + /** * Get the PropertyDescriptor for the given property name. * @@ -37,7 +56,8 @@ public interface PropertySource { /** - * Get the PropertyDescriptors for all defined properties. The properties are returned sorted by UI order. + * Get the descriptors of all defined properties. + * The properties are returned sorted by UI order. * * @return The PropertyDescriptors in UI order. */ @@ -45,7 +65,16 @@ public interface PropertySource { /** - * Get the typed value for the given property. Multi valued properties return immutable lists. + * Returns a modifiable list of the property descriptors + * that don't use default values. + * + * @return The descriptors that don't use default values + */ + List> getOverriddenPropertyDescriptors(); + + /** + * Get the typed value for the given property. + * Multi valued properties return immutable lists. * * @param The underlying type of the property descriptor. * @param propertyDescriptor The property descriptor. @@ -56,7 +85,18 @@ public interface PropertySource { /** - * Set the property value specified (will be type-checked) + * Returns true if the given property has been set to a value + * somewhere in the XML. + * + * @param propertyDescriptor The descriptor + * + * @return True if the property has been set + */ + boolean isPropertyOverridden(PropertyDescriptor propertyDescriptor); + + /** + * Set the property value specified. This is also referred to as "overriding" + * the (default) value of a property. * * @param The underlying type of the property descriptor. * @param propertyDescriptor The property descriptor. @@ -67,6 +107,7 @@ public interface PropertySource { /** * Sets the value of a multi value property descriptor with a variable number of arguments. + * This is also referred to as "overriding" the (default) value of a property. * * @param propertyDescriptor The property descriptor for which to add a value * @param values Values @@ -76,19 +117,36 @@ public interface PropertySource { /** - * Returns all the current property values for the receiver or an immutable empty map if none are specified. + * Returns an unmodifiable map of descriptors to property values + * for the current receiver. The returned map has an entry for + * every defined descriptor ({@link #getPropertyDescriptors()}), + * if they were not specified explicitly, then default values are + * used. * - * @return all current property values or a empty map. + * @return An unmodifiable map of descriptors to property values */ Map, Object> getPropertiesByPropertyDescriptor(); /** - * Returns whether this Rule has the specified PropertyDescriptor. + * Returns a modifiable map of the property descriptors + * that don't use default values, to their overridden value. + * Modifications on the returned map don't affect this property + * source. * - * @param descriptor The PropertyDescriptor for which to check. + * @return The descriptors that don't use default values + */ + Map, Object> getOverriddenPropertiesByPropertyDescriptor(); + + + /** + * Returns whether the specified property is defined on this property source, + * in which case it can be set or retrieved with {@link #getProperty(PropertyDescriptor)} + * and {@link #setProperty(PropertyDescriptor, Object)}. * - * @return boolean true if the descriptor is present, false otherwise. + * @param descriptor The descriptor to look for + * + * @return {@code true} if the descriptor is defined, {@code false} otherwise. */ boolean hasDescriptor(PropertyDescriptor descriptor); @@ -97,7 +155,10 @@ public interface PropertySource { * Returns whether this Rule uses default values for properties. * * @return boolean true if the properties all have default values, false otherwise. + * + * @deprecated Has no real utility, will be removed by 7.0.0 */ + @Deprecated boolean usesDefaultValues(); @@ -105,7 +166,10 @@ public interface PropertySource { * Clears out any user-specified value for the property allowing it to use the default value in the descriptor. * * @param desc the property to clear out + * + * @deprecated Has no real utility, and the name is confusing, will be removed by 7.0.0 */ + @Deprecated void useDefaultValueFor(PropertyDescriptor desc); @@ -114,13 +178,16 @@ public interface PropertySource { * properties. This can be used to disable corresponding widgets in a UI. * * @return the properties that are ignored + * @deprecated Has no real utility, will be removed by 7.0.0 */ + @Deprecated Set> ignoredProperties(); /** - * Returns a description of why the receiver may be dysfunctional. Usually due to missing property values or some - * kind of conflict between values. Returns null if the receiver is ok. + * Returns a description of why the receiver may be dysfunctional. + * Usually due to missing property values or some kind of conflict + * between values. Returns null if the receiver is ok. * * @return String */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java index 714aec5069..c7559693b9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java @@ -12,7 +12,7 @@ import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; /** - * Property which has a regex pattern as a value.This property has no multi-valued + * Property which has a regex pattern as a value. This property has no multi-valued * variant, since it would be ambiguous whether the delimiters are part of the regex * or not. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java index e01b788cda..33a7612495 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java @@ -27,6 +27,11 @@ public abstract class AbstractRenderer extends AbstractPropertySource implements this.description = description; } + @Override + protected String getPropertySourceType() { + return "renderer"; + } + @Override public String getName() { return name; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/Renderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/Renderer.java index 4e82bb43dc..c033f26f5b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/Renderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/Renderer.java @@ -40,6 +40,7 @@ public interface Renderer extends PropertySource { * * @return The name of the Renderer. */ + @Override String getName(); /** diff --git a/pmd-core/src/main/resources/rulesets/releases/680.xml b/pmd-core/src/main/resources/rulesets/releases/680.xml new file mode 100644 index 0000000000..e68c94a5a5 --- /dev/null +++ b/pmd-core/src/main/resources/rulesets/releases/680.xml @@ -0,0 +1,14 @@ + + + + +This ruleset contains links to rules that are new in PMD v6.8.0 + + + + + + diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index 954141a087..608b3d43e0 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-cs/pom.xml b/pmd-cs/pom.xml index 15e7b227bf..b2d49298c0 100644 --- a/pmd-cs/pom.xml +++ b/pmd-cs/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 4f93d6128e..d089bfc4d3 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java index 0317c043c3..c9c52d291e 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java @@ -93,11 +93,11 @@ public class BinaryDistributionIT { result = PMDExecutor.runPMD(tempDir, "-h"); result.assertExecutionResult(1, "apex, ecmascript, java, jsp, plsql, pom, vf, vm, wsdl, xml, xsl"); - result = PMDExecutor.runPMDRules(tempDir, srcDir, "java-basic"); + result = PMDExecutor.runPMDRules(tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml"); result.assertExecutionResult(4, "JumbledIncrementer.java:8:"); - result = PMDExecutor.runPMDRules(tempDir, srcDir, "java-design"); - result.assertExecutionResult(0, ""); + result = PMDExecutor.runPMDRules(tempDir, srcDir, "rulesets/java/quickstart.xml"); + result.assertExecutionResult(4, ""); } @Test diff --git a/pmd-dist/src/test/resources/rulesets/sample-ruleset.xml b/pmd-dist/src/test/resources/rulesets/sample-ruleset.xml new file mode 100644 index 0000000000..338b646921 --- /dev/null +++ b/pmd-dist/src/test/resources/rulesets/sample-ruleset.xml @@ -0,0 +1,10 @@ + + + Sample Ruleset for Integration Test + + + + diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml index 08a9c356a8..90818a3d1c 100644 --- a/pmd-doc/pom.xml +++ b/pmd-doc/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-fortran/pom.xml b/pmd-fortran/pom.xml index c068abea1b..a34de97223 100644 --- a/pmd-fortran/pom.xml +++ b/pmd-fortran/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-go/pom.xml b/pmd-go/pom.xml index 79d409113d..c92928f3cb 100644 --- a/pmd-go/pom.xml +++ b/pmd-go/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-groovy/pom.xml b/pmd-groovy/pom.xml index 30c9b97c70..c573e3fa3a 100644 --- a/pmd-groovy/pom.xml +++ b/pmd-groovy/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index 329c8036a7..e3fd94f551 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolation.java index be8622ae03..35b7814209 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolation.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolation.java @@ -9,13 +9,13 @@ import java.util.Set; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.AbstractAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.CanSuppressWarnings; import net.sourceforge.pmd.lang.java.ast.JavaNode; @@ -97,7 +97,7 @@ public class JavaRuleViolation extends ParametricRuleViolation { private void setClassNameFrom(JavaNode node) { String qualifiedName = null; - for (ASTClassOrInterfaceDeclaration parent : node.getParentsOfType(ASTClassOrInterfaceDeclaration.class)) { + for (AbstractAnyTypeDeclaration parent : node.getParentsOfType(AbstractAnyTypeDeclaration.class)) { String clsName = parent.getScope().getEnclosingScope(ClassScope.class).getClassName(); if (qualifiedName == null) { qualifiedName = clsName; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidUsingHardCodedIPRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidUsingHardCodedIPRule.java index a70cf3ce0e..6e51e2b5ad 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidUsingHardCodedIPRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidUsingHardCodedIPRule.java @@ -17,7 +17,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTLiteral; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.properties.EnumeratedMultiProperty; -import net.sourceforge.pmd.properties.PropertySource; public class AvoidUsingHardCodedIPRule extends AbstractJavaRule { @@ -217,9 +216,7 @@ public class AvoidUsingHardCodedIPRule extends AbstractJavaRule { return getProperty(CHECK_ADDRESS_TYPES_DESCRIPTOR).size() > 0; } - /** - * @see PropertySource#dysfunctionReason() - */ + @Override public String dysfunctionReason() { return hasChosenAddressTypes() ? null : "No address types specified"; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryModifierRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryModifierRule.java index ea61b03767..79a85447cb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryModifierRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryModifierRule.java @@ -169,7 +169,7 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { if ((node.isInterface() || isParentInterfaceOrAnnotation) && node.isStatic()) { // a static interface or class nested within an interface - reportUnnecessaryModifiers(data, node, Modifier.PUBLIC, "types nested within an interface type are implicitly static"); + reportUnnecessaryModifiers(data, node, Modifier.STATIC, "types nested within an interface type are implicitly static"); } return data; diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index e6fdcd2de2..f1b58e94bb 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -2294,6 +2294,8 @@ public void bar(int status) { externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#missingserialversionuid"> Serializable classes should provide a serialVersionUID field. +The serialVersionUID field is also needed for abstract base classes. Each individual class in the inheritance +chain needs an own serialVersionUID field. See also [Should an abstract class have a serialVersionUID](https://stackoverflow.com/questions/893259/should-an-abstract-class-have-a-serialversionuid). 3 @@ -2301,7 +2303,7 @@ Serializable classes should provide a serialVersionUID field. + + Quickstart configuration of PMD. Includes the rules that are most likely to apply everywhere. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolationTest.java index 06174cd3ba..b9c690f12d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/JavaRuleViolationTest.java @@ -63,6 +63,18 @@ public class JavaRuleViolationTest { assertEquals("bar", violation.getMethodName()); } + /** + * Tests that the enum name is taken correctly from the given node. + */ + @Test + public void testEnumName() { + ASTCompilationUnit ast = parse("enum Foo {FOO; void bar(int x) {} }"); + ASTMethodDeclaration md = ast.getFirstDescendantOfType(ASTMethodDeclaration.class); + final RuleContext context = new RuleContext(); + final JavaRuleViolation violation = new JavaRuleViolation(null, context, md, null); + assertEquals("Foo", violation.getClassName()); + } + /** * Tests that the class name is taken correctly, even if the node is outside * of a class scope, e.g. a import declaration. diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryModifier.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryModifier.xml index c3212d803a..936436a89e 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryModifier.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryModifier.xml @@ -673,6 +673,20 @@ enum Foo { private Foo(String s) { name = s; } +} + ]]> + + + Static Modifier on interface + 1 + + Unnecessary modifier 'static' on interface 'Bar': types nested within an interface type are implicitly static + + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingSerialVersionUID.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingSerialVersionUID.xml index 0257f94ee3..0a6ac38ecb 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingSerialVersionUID.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/MissingSerialVersionUID.xml @@ -67,16 +67,24 @@ public interface Foo implements Bar{ ]]> - - 0 + abstract case, see #1352 + 1 + + inherited abstract case + 1 + + + + + + #1350 [java] MissingSerialVersionUID false-positive on interfaces + 0 + > extends MutablePrimaryIdentifier, Serializable { } ]]> diff --git a/pmd-java8/pom.xml b/pmd-java8/pom.xml index b9fda3ebcc..cabf0b04c0 100644 --- a/pmd-java8/pom.xml +++ b/pmd-java8/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index bacddc1155..8f8be8b63b 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptions.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptions.java index de549e7524..e616857df1 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptions.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptions.java @@ -4,13 +4,14 @@ package net.sourceforge.pmd.lang.ecmascript; +import java.util.Objects; + import org.mozilla.javascript.Context; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.properties.BooleanProperty; import net.sourceforge.pmd.properties.EnumeratedProperty; -import net.sourceforge.pmd.util.StringUtil; public class EcmascriptParserOptions extends ParserOptions { @@ -120,7 +121,7 @@ public class EcmascriptParserOptions extends ParserOptions { return false; } final EcmascriptParserOptions that = (EcmascriptParserOptions) obj; - return StringUtil.isSame(this.suppressMarker, that.suppressMarker, false, false, false) + return Objects.equals(this.suppressMarker, that.suppressMarker) && this.recordingComments == that.recordingComments && this.recordingLocalJsDocComments == that.recordingLocalJsDocComments && this.rhinoLanguageVersion == that.rhinoLanguageVersion; diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index 897bbcd3cc..2ea336d241 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml index 08d1f1abf8..ba529af5e5 100644 --- a/pmd-matlab/pom.xml +++ b/pmd-matlab/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml index 868abfc638..90cda59020 100644 --- a/pmd-objectivec/pom.xml +++ b/pmd-objectivec/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-perl/pom.xml b/pmd-perl/pom.xml index 527f8ba177..864a2b756e 100644 --- a/pmd-perl/pom.xml +++ b/pmd-perl/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-php/pom.xml b/pmd-php/pom.xml index 843498aacd..695620e830 100644 --- a/pmd-php/pom.xml +++ b/pmd-php/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.8.0-SNAPSHOT + 6.9.0-SNAPSHOT diff --git a/pmd-plsql/etc/grammar/PldocAST.jjt b/pmd-plsql/etc/grammar/PldocAST.jjt index f265be2476..2373e32f6e 100644 --- a/pmd-plsql/etc/grammar/PldocAST.jjt +++ b/pmd-plsql/etc/grammar/PldocAST.jjt @@ -27,7 +27,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** + * Added support for DELETE Statements + * + * Andreas Dangel 09/2018 + *==================================================================== + * Added support for OrderBy and RowLimiting clauses for SELECT statements + * Removed FROM from the RelationalExpression + * Support QueryPartitionClause + * Support GroupByClause + * + * Andreas Dangel 08/2018 + *==================================================================== * Added more complete support for CREATE TABLE + * Added support for SELECT INTO statement + * Avoiding deep AST for *Expression nodes * Added ASTCursorForLoop and ASTSelectStatement * * Andreas Dangel 07/2018 @@ -51,7 +64,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // options { - DEBUG_PARSER = false ; + DEBUG_PARSER = false; DEBUG_TOKEN_MANAGER = false; DEBUG_LOOKAHEAD = false; IGNORE_CASE = true; @@ -204,7 +217,9 @@ ASTInput Input() : {} | LOOKAHEAD(6) DatabaseLink() | LOOKAHEAD(6) Global() | LOOKAHEAD(6) DDLCommand() //Ignore any other DDL Event - | LOOKAHEAD(2) SqlPlusCommand() + | LOOKAHEAD(2) SqlPlusCommand() + | LOOKAHEAD(2) UpdateStatement() + | LOOKAHEAD(2) DeleteStatement() |( [ { jjtThis.setDistinct(true); } + | { jjtThis.setUnique(true); } + | { jjtThis.setAll(true); } ] + + SelectList() + ( IntoClause() | BulkCollectIntoClause() ) + RestOfStatement() + + { return jjtThis; } +} + +void RestOfStatement() #void : +{} +{ + FromClause() + [ WhereClause() ] + [ GroupByClause() ] + [ OrderByClause() ] + [ RowLimitingClause() ] +} + +void Subquery() #void : +{} +{ + ( + QueryBlock() (LOOKAHEAD(2) ( SubqueryOperation() ) Subquery() )* + | "(" Subquery() ")" + ) + [ LOOKAHEAD(2) OrderByClause() ] + [ LOOKAHEAD(2) RowLimitingClause() ] +} + +ASTSubqueryOperation SubqueryOperation() : +{} +{ + ( + { jjtThis.setImage(token.image); jjtThis.setUnion(true); } [ { jjtThis.setImage(jjtThis.getImage() + " " + token.image); jjtThis.setAll(true); } ] + | + { jjtThis.setImage(token.image); jjtThis.setIntersect(true); } + | + { jjtThis.setImage(token.image); jjtThis.setMinus(true); } + ) + { return jjtThis; } +} + +/** + * @see https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/img_text/order_by_clause.html + */ +ASTOrderByClause OrderByClause() : +{} +{ + [ ] + OrderByEntry() ( "," OrderByEntry() )* + { return jjtThis; } +} + +void OrderByEntry() #void : +{} +{ + ( LOOKAHEAD(2) ColumnAlias() | LOOKAHEAD(2) SqlExpression() ) + [ | ] + [ LOOKAHEAD(2) | LOOKAHEAD(2) ] +} + +/** + * @see https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/img_text/row_limiting_clause.html + */ +ASTRowLimitingClause RowLimitingClause() : +{} +{ + ( + NumericLiteral() ( | ) + | + ( | ) [ NumericLiteral() [ ] ] ( | ) ( | ) + ) + { return jjtThis; } +} + +ASTQueryBlock QueryBlock() : +{} +{ + // [ WithClause() ] + |||||||||||) SqlStatement(null,";") [";"] | LOOKAHEAD(3) ContinueStatement() ";" // CONTINUE keyword was added in 11G, so Oracle compilation supports CONTINUE as a variable name | CaseStatement() ";" @@ -1289,6 +1837,36 @@ ASTSelectStatement SelectStatement() : { return jjtThis ; } } +ASTUpdateStatement UpdateStatement() : +{} +{ + SqlExpression() + UpdateSetClause() + [ WhereClause() ] + { return jjtThis; } +} + +ASTUpdateSetClause UpdateSetClause() : +{} +{ + + ( + "=" ID() + | + Column() "=" ( Expression() | <_DEFAULT> ) + ) + { return jjtThis; } +} + +ASTDeleteStatement DeleteStatement() : +{} +{ + [ ] + ( TableReference() | "(" TableReference() ")" ) + [ WhereClause() ] + { return jjtThis; } +} + /** Scope rule: the loop index only exists within the Loop */ ASTForStatement ForStatement() : {} @@ -1813,7 +2391,7 @@ ASTObjectExpression ObjectExpression() : } } -ASTConditionalOrExpression ConditionalOrExpression() : +ASTConditionalOrExpression ConditionalOrExpression() #ConditionalOrExpression(>1) : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( @@ -1827,7 +2405,7 @@ ASTConditionalOrExpression ConditionalOrExpression() : } } -ASTConditionalAndExpression ConditionalAndExpression() : +ASTConditionalAndExpression ConditionalAndExpression() #ConditionalAndExpression(>1) : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( @@ -1841,7 +2419,7 @@ ASTConditionalAndExpression ConditionalAndExpression() : } } -ASTEqualityExpression EqualityExpression() : +ASTEqualityExpression EqualityExpression() #EqualityExpression(>1) : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { //RelationalExpression() ( ( "=" | "!=" | "<>" | ) RelationalExpression() )* @@ -1865,14 +2443,11 @@ ASTEqualityExpression EqualityExpression() : } } -/** - * 2006-05-23 - Matthias Hendler - added FROM - */ -ASTRelationalExpression RelationalExpression() : +ASTRelationalExpression RelationalExpression() #RelationalExpression(>1) : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( - //AdditiveExpression() ( ( "<" | ">" | "<=" | ">=" | [] ( | | | ) ) AdditiveExpression() )* + //AdditiveExpression() ( ( "<" | ">" | "<=" | ">=" | [] ( | | ) ) AdditiveExpression() )* (simpleNode = AdditiveExpression() ) { sb.append(simpleNode.getImage()); } ( ( @@ -1891,7 +2466,6 @@ ASTRelationalExpression RelationalExpression() : (() { sb.append(" IN "); } | ( ) { sb.append(" BETWEEN "); } | ( ) { sb.append(" LIKE "); } - | () { sb.append(" FROM "); } | ( ( () { sb.append(" MEMBER "); } @@ -1927,7 +2501,7 @@ ASTRelationalExpression RelationalExpression() : } } -ASTAdditiveExpression AdditiveExpression() : +ASTAdditiveExpression AdditiveExpression() #AdditiveExpression(>1) : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( @@ -1960,7 +2534,7 @@ ASTStringExpression StringExpression() : } } -ASTMultiplicativeExpression MultiplicativeExpression() : +ASTMultiplicativeExpression MultiplicativeExpression() #MultiplicativeExpression(>1) : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { @@ -1981,7 +2555,7 @@ ASTMultiplicativeExpression MultiplicativeExpression() : } } -ASTUnaryExpression UnaryExpression(boolean isUnarySign) : +ASTUnaryExpression UnaryExpression(boolean isUnarySign) #UnaryExpression(>1) : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( @@ -1997,7 +2571,7 @@ ASTUnaryExpression UnaryExpression(boolean isUnarySign) : } } -ASTUnaryExpressionNotPlusMinus UnaryExpressionNotPlusMinus() : +ASTUnaryExpressionNotPlusMinus UnaryExpressionNotPlusMinus() #UnaryExpressionNotPlusMinus(>1) : { PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { ( @@ -2010,15 +2584,15 @@ ASTUnaryExpressionNotPlusMinus UnaryExpressionNotPlusMinus() : jjtThis.setImage(sb.toString()); return jjtThis; } } -ASTIsNullCondition IsNullCondition() : //yanzin +ASTIsNullCondition IsNullCondition() #IsNullCondition(>1) : //yanzin { PLSQLNode simpleNode = null; PLSQLNode name = null; StringBuilder sb = new StringBuilder(); } { ( - LOOKAHEAD( ( |)) + LOOKAHEAD( [] ) ( (name = Name()) {sb.append(name.getImage());} {sb.append(" IS");} [ {sb.append(" NOT");}] {sb.append(" NULL");} ) - | + | ( simpleNode = IsOfTypeCondition() ) @@ -2032,13 +2606,18 @@ ASTIsNullCondition IsNullCondition() : //yanzin } } -ASTIsOfTypeCondition IsOfTypeCondition() : +ASTIsOfTypeCondition IsOfTypeCondition() #IsOfTypeCondition(>1) : { PLSQLNode simpleNode = null; PLSQLNode name = null; StringBuilder sb = new StringBuilder(); } { ( LOOKAHEAD( ) ((name = Name()) {sb.append(name.getImage());} {sb.append(" IS");} [ {sb.append(" NOT");}] {sb.append(" OF");} [] "(" [] Name() ("," [] Name() )* ")") + | + LOOKAHEAD(PrimaryExpression() ) + (simpleNode = PrimaryExpression() ) { sb.append(simpleNode.getImage()); } + {sb.append(" IS");} [ {sb.append(" NOT");}] {sb.append(" OF");} [] + "(" [] Name() ("," [] Name() )* ")" | (simpleNode = PrimaryExpression() ) { sb.append(simpleNode.getImage()); } ) @@ -2052,7 +2631,7 @@ ASTIsOfTypeCondition IsOfTypeCondition() : * Warning arised while adding methode triggerUnit(). * 2011-04-27 - SRT - Add optional NEW Keyword to cope with Object Type constructors */ -ASTPrimaryExpression PrimaryExpression() : +ASTPrimaryExpression PrimaryExpression() #PrimaryExpression(>1) : { Token thisToken ; PLSQLNode simpleNode = null; StringBuilder sb = new StringBuilder() ; } { @@ -3352,6 +3931,7 @@ TOKEN [IGNORE_CASE]: | | | + | | | | @@ -3387,6 +3967,8 @@ TOKEN [IGNORE_CASE]: | | | + | + | | | | @@ -3427,16 +4009,19 @@ TOKEN [IGNORE_CASE]: | | | + | | | | | | | + | | | | | + | | //SRT 2011-04-17 | | @@ -3446,12 +4031,14 @@ TOKEN [IGNORE_CASE]: | | | + | | | | | | | + | | | | @@ -3463,10 +4050,15 @@ TOKEN [IGNORE_CASE]: | | | + | | // | + | | | + | + | + | | | | @@ -3485,10 +4077,12 @@ TOKEN [IGNORE_CASE]: | | | + | | | | | + | | | | //SRT 2011-04-17 @@ -3499,6 +4093,7 @@ TOKEN [IGNORE_CASE]: | | | + | | | | @@ -3509,6 +4104,7 @@ TOKEN [IGNORE_CASE]: | | | + | | | | @@ -3521,11 +4117,13 @@ TOKEN [IGNORE_CASE]: | | | + | | | | | | + | | | | @@ -3558,6 +4156,7 @@ TOKEN [IGNORE_CASE]: | | //SRT 2011-04-17 | + | | | | @@ -3570,11 +4169,14 @@ TOKEN [IGNORE_CASE]: | | | + | + | | //SRT 2011-04-17 | | | | + | | | | @@ -3592,6 +4194,7 @@ TOKEN [IGNORE_CASE]: | | | + | | | | @@ -3618,8 +4221,7 @@ TOKEN [IGNORE_CASE]: | // are they reserved or not ? -// most are not reserved, but cannot use just "WHERE" etc instead - resolves as identifier ! -// | +// most are not reserved, but cannot use just define them - might be resolved as identifiers // | // | | @@ -3941,7 +4543,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| //| //| //| test_unreserved_keyword.pks @@ -4072,8 +4674,8 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| -//| +| +| //| | //| @@ -4204,7 +4806,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} | //| //| -//| +| //| //| //| @@ -4219,7 +4821,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| | //| //| @@ -4231,7 +4833,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| //| //| //| @@ -4288,7 +4890,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| | | // FORALL i I INDICES OF collection - SPARSE COLLECTIONS -//| +| //| //| //| @@ -4296,7 +4898,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| //| //| | @@ -4315,7 +4917,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| | //| -//| +| //| //| //| @@ -4325,7 +4927,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -//| +| //| //| //| @@ -4339,9 +4941,9 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| | //| -//| -//| -//| +| +| +| | | //| @@ -4423,7 +5025,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| | //| -//| +| //| //| //| @@ -4442,7 +5044,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -//| +| //| //| //| @@ -4545,7 +5147,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| //| | | @@ -4554,6 +5156,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| +| | //| | @@ -4579,7 +5182,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} | | //| -//| +| //| //| //| @@ -4616,7 +5219,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| //| //| //| @@ -4734,7 +5337,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| | //| -//| +| | //| //| @@ -4783,7 +5386,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| | //SET is defined as a reserved word but is used in "SYS"."DBMS_RESULT_CACHE_API" as a function name and as a Pragma parameter //| -//| +| //| //| | @@ -4792,7 +5395,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -//| +| //| //| //| @@ -4803,7 +5406,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| //| //| | @@ -4878,6 +5481,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| +| |