[
  {
    "path": ".gitattributes",
    "content": "* text=auto\n*.java text\n*.md text\n*.xml text\n*.xsd text\n*.xsl text\n*.bat text eol=crlf\n*.sh text eol=lf\n"
  },
  {
    "path": ".gitignore",
    "content": "/huntbugstest\n/target\n\n# Eclipse workspace\n.metadata\n\n# Merge files\n*.orig\n\n# IntelliJ IDEA\n.idea\n\n*.releaseBackup"
  },
  {
    "path": ".mailmap",
    "content": "Tagir Valeev <lany@ngs.ru> <lan@developmentontheedge.com>"
  },
  {
    "path": ".travis.yml",
    "content": "language: java\ninstall:\n  - mvn install -DskipTests=true -B -V\njdk:\n  - oraclejdk8\nafter_success:\n  - cd huntbugs && mvn jacoco:report coveralls:report"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\n\nVersion 2.0, January 2004\n\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\n    You must give any other recipients of the Work or Derivative Works a copy of this License; and\n    You must cause any modified files to carry prominent notices stating that You changed the files; and\n    You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\n    If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.\n\n    You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS"
  },
  {
    "path": "README.md",
    "content": "This project is abandoned and unlikely will be supported in future\n===\n\nHuntBugs 0.0.11\n===\n\n[![Join the chat at https://gitter.im/amaembo/huntbugs](https://badges.gitter.im/amaembo/huntbugs.svg)](https://gitter.im/amaembo/huntbugs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n[![Maven Central](https://img.shields.io/maven-central/v/one.util/huntbugs.svg)](https://maven-badges.herokuapp.com/maven-central/one.util/huntbugs/)\n[![Build Status](https://travis-ci.org/amaembo/huntbugs.png?branch=master)](https://travis-ci.org/amaembo/huntbugs)\n[![Coverage Status](https://coveralls.io/repos/github/amaembo/huntbugs/badge.svg?branch=master)](https://coveralls.io/github/amaembo/huntbugs?branch=master)\n\nNew Java bytecode static analyzer tool based on [Procyon Compiler Tools](https://bitbucket.org/mstrobel/procyon/overview) aimed to supersede the [FindBugs](http://findbugs.sourceforge.net/).\nCurrently in early development stage, though already could be tried.\n\nCurrently 222 FindBugs warnings reimplemented and several new warnings added.\n\n### Use with Maven\n\nCompile project and run `mvn one.util:huntbugs-maven-plugin:huntbugs`\n\nThe output report is located in `target/huntbugs/report.html`\n\n### Use with Ant\n\n* Build `huntbugs-ant-plugin` via `mvn package` (or alternatively download from [here](https://oss.sonatype.org/content/repositories/releases/one/util/huntbugs-ant-plugin/))\n* Take the resulting `huntbugs-ant-plugin-<version>-nodeps.jar`\n* Define the task:\n\n~~~~xml\n<taskdef resource=\"one/util/huntbugs/ant/antlib.xml\">\n    <classpath path=\"path/to/huntbugs-ant-plugin-<version>-nodeps.jar\"/>\n</taskdef>\n~~~~\n\n* Run it:\n\n~~~~xml\n<huntbugs classPath=\"${MY_APP_CLASSPATH}\" \n          auxClassPath=\"${DEPS_CLASSPATH}\" \n          html=\"path/to/html/report.html\" \n          xml=\"path/to/xml/report.xml\"/>\n~~~~\n\n### Use with Gradle\n\nCheck the [Gradle plugin page](https://github.com/lavcraft/huntbugs-gradle-plugin)\n\n### Use with Eclipse\n\nCheck the [Eclipse plugin page](https://github.com/aaasko/huntbugs-eclipse) (in early development stage)\n\n### Exec as command-line tool\n\nCommand-line tool is mostly aimed to aid developers. Nevertheless you may use it if you like.\nTo launch use `mvn exec:java -Dexec.args=\"...args...\"` inside huntbugs subdirectory. Examples:\n\n* `mvn exec:java -Dexec.args=\"-lw\"` will list all the warnings.\n* `mvn exec:java -Dexec.args=\"myfolder/*.jar\"` will analyze all jars inside `myfolder` writing the report into `huntbugs.warnings.xml` and `huntbugs.warnings.html` in current directory.\n* `mvn exec:java` will show all the supported command line options.\n"
  },
  {
    "path": "findbugs.txt",
    "content": "==== Reimplemented\nabbrev=\"CNT\" type=\"CNT_ROUGH_CONSTANT_VALUE\" category=\"BAD_PRACTICE\" --> RoughConstantValue\nabbrev=\"NS\" type=\"NS_NON_SHORT_CIRCUIT\" category=\"STYLE\" --> NonShortCircuit\nabbrev=\"NS\" type=\"NS_DANGEROUS_NON_SHORT_CIRCUIT\" category=\"STYLE\" --> NonShortCircuitDangerous\nabbrev=\"Dm\" type=\"DM_NEW_FOR_GETCLASS\" category=\"PERFORMANCE\" --> NewForGetClass\nabbrev=\"VO\" type=\"VO_VOLATILE_INCREMENT\" category=\"MT_CORRECTNESS\" --> VolatileIncrement, VolatileMath\nabbrev=\"FI\" type=\"FI_NULLIFY_SUPER\" category=\"BAD_PRACTICE\" --> FinalizeNullifiesSuper\nabbrev=\"FI\" type=\"FI_USELESS\" category=\"BAD_PRACTICE\" --> FinalizeUselessSuper\nabbrev=\"FI\" type=\"FI_EMPTY\" category=\"BAD_PRACTICE\" --> FinalizeEmpty\nabbrev=\"FI\" type=\"FI_PUBLIC_SHOULD_BE_PROTECTED\" category=\"MALICIOUS_CODE\" cweid=\"583\" --> FinalizePublic\nabbrev=\"FI\" type=\"FI_EXPLICIT_INVOCATION\" category=\"BAD_PRACTICE\" cweid=\"586\" --> FinalizeInvocation\nabbrev=\"FE\" type=\"FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER\" category=\"CORRECTNESS\" --> FloatCompareToNaN\nabbrev=\"FE\" type=\"FE_FLOATING_POINT_EQUALITY\" category=\"STYLE\" --> FloatComparison\nabbrev=\"Bx\" type=\"DM_NUMBER_CTOR\" category=\"PERFORMANCE\" --> NumberConstructor\nabbrev=\"Dm\" type=\"DM_BOOLEAN_CTOR\" category=\"PERFORMANCE\" --> BooleanConstructor\nabbrev=\"Dm\" type=\"DM_INVALID_MIN_MAX\" category=\"CORRECTNESS\" --> InvalidMinMax\nabbrev=\"SIO\" type=\"SIO_SUPERFLUOUS_INSTANCEOF\" category=\"CORRECTNESS\" --> UnnecessaryInstanceOf\nabbrev=\"RV\" type=\"RV_ABSOLUTE_VALUE_OF_RANDOM_INT\" category=\"CORRECTNESS\" --> AbsoluteValueOfRandomInt\nabbrev=\"RV\" type=\"RV_ABSOLUTE_VALUE_OF_HASHCODE\" category=\"CORRECTNESS\" --> AbsoluteValueOfHashCode\nabbrev=\"RV\" type=\"RV_01_TO_INT\" category=\"CORRECTNESS\" --> RandomDoubleToInt\nabbrev=\"Dm\" type=\"DM_NEXTINT_VIA_NEXTDOUBLE\" category=\"PERFORMANCE\" --> RandomNextIntViaNextDouble\nabbrev=\"Dm\" type=\"DM_STRING_CTOR\" category=\"PERFORMANCE\" --> StringConstructor\nabbrev=\"Dm\" type=\"DM_STRING_VOID_CTOR\" category=\"PERFORMANCE\" --> StringConstructorEmpty\nabbrev=\"Dm\" type=\"DM_STRING_TOSTRING\" category=\"PERFORMANCE\" --> StringToString\nabbrev=\"Dm\" type=\"DM_EXIT\" category=\"BAD_PRACTICE\" cweid=\"382\" --> SystemExit\nabbrev=\"Eq\" type=\"EQ_ALWAYS_TRUE\" category=\"CORRECTNESS\" --> EqualsReturnsTrue\nabbrev=\"Eq\" type=\"EQ_ALWAYS_FALSE\" category=\"CORRECTNESS\" --> EqualsReturnsFalse\nabbrev=\"SA\" type=\"SA_FIELD_SELF_ASSIGNMENT\" category=\"CORRECTNESS\" --> SelfAssignmentField\nabbrev=\"Dm\" type=\"DM_GC\" category=\"PERFORMANCE\" --> System.gc()\nabbrev=\"INT\" type=\"INT_BAD_REM_BY_1\" category=\"STYLE\" --> RemOne\nabbrev=\"INT\" type=\"INT_VACUOUS_BIT_OPERATION\" category=\"STYLE\" --> UselessOrWithZero, UselessAndWithMinusOne\nabbrev=\"SA\" type=\"SA_FIELD_SELF_COMPUTATION\" category=\"CORRECTNESS\" --> SelfComputation\nabbrev=\"SA\" type=\"SA_LOCAL_SELF_COMPUTATION\" category=\"CORRECTNESS\" --> SelfComputation\nabbrev=\"SA\" type=\"SA_FIELD_SELF_COMPARISON\" category=\"CORRECTNESS\" --> SelfComparison\nabbrev=\"SA\" type=\"SA_LOCAL_SELF_COMPARISON\" category=\"CORRECTNESS\" --> SelfComparison\nabbrev=\"BIT\" type=\"BIT_AND\" category=\"CORRECTNESS\" --> CompareBitAndIncompatible\nabbrev=\"BIT\" type=\"BIT_IOR\" category=\"CORRECTNESS\" --> CompareBitOrIncompatible\nabbrev=\"FI\" type=\"FI_FINALIZER_ONLY_NULLS_FIELDS\" category=\"BAD_PRACTICE\" --> FinalizeOnlyNullsFields\nabbrev=\"FI\" type=\"FI_FINALIZER_NULLS_FIELDS\" category=\"BAD_PRACTICE\" --> FinalizeNullsFields\nabbrev=\"Dm\" type=\"DM_USELESS_THREAD\" category=\"MT_CORRECTNESS\" --> UselessThread\nabbrev=\"UW\" type=\"UW_UNCOND_WAIT\" category=\"MT_CORRECTNESS\" --> WaitUnconditional\nabbrev=\"Wa\" type=\"WA_NOT_IN_LOOP\" category=\"MT_CORRECTNESS\" --> WaitNotInLoop\nabbrev=\"DB\" type=\"DB_DUPLICATE_BRANCHES\" category=\"STYLE\" --> SameBranchesIf, SameBranchesTernary\nabbrev=\"UCF\" type=\"UCF_USELESS_CONTROL_FLOW\" category=\"STYLE\" --> EmptyBranch\nabbrev=\"UCF\" type=\"UCF_USELESS_CONTROL_FLOW_NEXT_LINE\" category=\"STYLE\" --> EmptyBranch\nabbrev=\"Dm\" type=\"DM_RUN_FINALIZERS_ON_EXIT\" category=\"BAD_PRACTICE\" --> SystemRunFinalizersOnExit\nabbrev=\"NN\" type=\"NN_NAKED_NOTIFY\" category=\"MT_CORRECTNESS\" --> NotifyNaked\nabbrev=\"DMI\" type=\"DMI_RANDOM_USED_ONLY_ONCE\" category=\"BAD_PRACTICE\" --> RandomUsedOnlyOnce\nabbrev=\"DB\" type=\"DB_DUPLICATE_SWITCH_CLAUSES\" category=\"STYLE\" --> SameBranchesSwitch, SameBranchesSwitchDefault\nabbrev=\"DMI\" type=\"DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE\" category=\"CORRECTNESS\" --> BigDecimalConstructedFromDouble\nabbrev=\"RpC\" type=\"RpC_REPEATED_CONDITIONAL_TEST\" category=\"CORRECTNESS\" --> SameConditions\nabbrev=\"Dm\" type=\"DMI_BLOCKING_METHODS_ON_URL\" category=\"PERFORMANCE\" --> URLBlockingMethod\nabbrev=\"SC\" type=\"SC_START_IN_CTOR\" category=\"MT_CORRECTNESS\" --> StartInConstructor\nabbrev=\"NP\" type=\"NP_BOOLEAN_RETURN_NULL\" category=\"BAD_PRACTICE\" --> BooleanReturnNull\nabbrev=\"NP\" type=\"NP_OPTIONAL_RETURN_NULL\" category=\"CORRECTNESS\" --> OptionalReturnNull\nabbrev=\"PZLA\" type=\"PZLA_PREFER_ZERO_LENGTH_ARRAYS\" category=\"STYLE\" --> ArrayReturnNull\nabbrev=\"Nm\" type=\"NM_METHOD_NAMING_CONVENTION\" category=\"BAD_PRACTICE\" --> BadNameOfMethod\nabbrev=\"AT\" type=\"AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION\" category=\"MT_CORRECTNESS\" --> NonAtomicOperationOnConcurrentMap\nabbrev=\"ICAST\" type=\"ICAST_INTEGER_MULTIPLY_CAST_TO_LONG\" category=\"STYLE\" --> IntegerMultiplicationPromotedToLong\nabbrev=\"ICAST\" type=\"ICAST_IDIV_CAST_TO_DOUBLE\" category=\"STYLE\" --> IntegerDivisionPromotedToFloat\nabbrev=\"ICAST\" type=\"ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL\" category=\"CORRECTNESS\" --> IntegerPromotionInCeilOrRound\nabbrev=\"ICAST\" type=\"ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND\" category=\"CORRECTNESS\" --> IntegerPromotionInCeilOrRound\nabbrev=\"EI2\" type=\"EI_EXPOSE_REP2\" category=\"MALICIOUS_CODE\" cweid=\"374\" --> ExposeMutableFieldViaParameter\nabbrev=\"MS\" type=\"EI_EXPOSE_STATIC_REP2\" category=\"MALICIOUS_CODE\" --> ExposeMutableStaticFieldViaParameter\nabbrev=\"USELESS_STRING\" type=\"DMI_INVOKING_TOSTRING_ON_ARRAY\" category=\"CORRECTNESS\" --> ArrayToString\nabbrev=\"USELESS_STRING\" type=\"DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY\" category=\"CORRECTNESS\" --> ArrayToString\nabbrev=\"DMI\" type=\"DMI_INVOKING_HASHCODE_ON_ARRAY\" category=\"CORRECTNESS\" --> ArrayHashCode\nabbrev=\"SA\" type=\"SA_LOCAL_SELF_ASSIGNMENT\" category=\"STYLE\" --> SelfAssignmentLocal\nabbrev=\"SA\" type=\"SA_LOCAL_SELF_ASSIGNMENT_INSTEAD_OF_FIELD\" category=\"CORRECTNESS\" --> SelfAssignmentLocalInsteadOfField\nabbrev=\"DMI\" type=\"DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT\" category=\"CORRECTNESS\"\nabbrev=\"BC\" type=\"BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY\" category=\"CORRECTNESS\" cweid=\"570\" --> ImpossibleToArrayDowncast\nabbrev=\"WMI\" type=\"WMI_WRONG_MAP_ITERATOR\" category=\"PERFORMANCE\" --> WrongMapIterator, WrongMapIteratorValues\nabbrev=\"UI\" type=\"UI_INHERITANCE_UNSAFE_GETRESOURCE\" category=\"BAD_PRACTICE\" --> UnsafeGetResource\nabbrev=\"RV\" type=\"RV_REM_OF_RANDOM_INT\" category=\"STYLE\" --> RandomIntRemainder\nabbrev=\"RV\" type=\"RV_REM_OF_HASHCODE\" category=\"STYLE\" --> HashCodeRemainder\nabbrev=\"ST\" type=\"ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD\" category=\"STYLE\" --> StaticFieldFromInstanceMethod\nabbrev=\"IMSE\" type=\"IMSE_DONT_CATCH_IMSE\" category=\"BAD_PRACTICE\" --> CatchIllegalMonitorStateException\nabbrev=\"BIT\" type=\"BIT_SIGNED_CHECK\" category=\"BAD_PRACTICE\" --> BitCheckGreater\nabbrev=\"BIT\" type=\"BIT_SIGNED_CHECK_HIGH_BIT\" category=\"CORRECTNESS\" --> BitCheckGreaterNegative\nabbrev=\"Bx\" type=\"DM_BOXED_PRIMITIVE_TOSTRING\" category=\"PERFORMANCE\" --> BoxedForToString\nabbrev=\"Bx\" type=\"BX_BOXING_IMMEDIATELY_UNBOXED\" category=\"PERFORMANCE\" --> BoxedForUnboxing\nabbrev=\"Bx\" type=\"BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION\" category=\"PERFORMANCE\" --> BoxedForUnboxing\nabbrev=\"Bx\" type=\"BX_UNBOXING_IMMEDIATELY_REBOXED\" category=\"PERFORMANCE\" --> UnboxedForBoxing\nabbrev=\"Nm\" type=\"NM_LCASE_HASHCODE\" category=\"CORRECTNESS\" --> BadNameOfMethodMistake\nabbrev=\"Nm\" type=\"NM_LCASE_TOSTRING\" category=\"CORRECTNESS\" --> BadNameOfMethodMistake\nabbrev=\"Nm\" type=\"NM_BAD_EQUAL\" category=\"CORRECTNESS\" --> BadNameOfMethodMistake\nabbrev=\"RV\" type=\"RV_NEGATING_RESULT_OF_COMPARETO\" category=\"BAD_PRACTICE\" --> NegatingComparatorResult\nabbrev=\"RV\" type=\"RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE\" category=\"CORRECTNESS\" --> ComparingComparatorResultWithNumber\nabbrev=\"FI\" type=\"FI_MISSING_SUPER_CALL\" category=\"BAD_PRACTICE\" --> FinalizeNoSuperCall\nabbrev=\"RV\" type=\"RV_EXCEPTION_NOT_THROWN\" category=\"CORRECTNESS\" --> DroppedException\nabbrev=\"Dm\" type=\"DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR\" category=\"CORRECTNESS\" --> ScheduledThreadPoolExecutorChangePoolSize\nabbrev=\"IO\" type=\"IO_APPENDING_TO_OBJECT_OUTPUT_STREAM\" category=\"CORRECTNESS\" --> AppendObjectOutputStream\nabbrev=\"DMI\" type=\"DMI_BAD_MONTH\" category=\"CORRECTNESS\" --> DateBadMonth\nabbrev=\"DMI\" type=\"DMI_USELESS_SUBSTRING\" category=\"STYLE\" --> UselessStringSubstring\nabbrev=\"RANGE\" type=\"RANGE_STRING_INDEX\" category=\"CORRECTNESS\" --> StringIndexIsLessThanZero, StringIndexIsGreaterThanAllowed\nabbrev=\"Se\" type=\"SE_COMPARATOR_SHOULD_BE_SERIALIZABLE\" category=\"BAD_PRACTICE\" --> ComparatorIsNotSerializable\nabbrev=\"Nm\" type=\"NM_CLASS_NAMING_CONVENTION\" category=\"BAD_PRACTICE\" --> BadNameOfClass\nabbrev=\"Nm\" type=\"NM_CLASS_NOT_EXCEPTION\" category=\"BAD_PRACTICE\" --> BadNameOfClassException\nabbrev=\"RI\" type=\"RI_REDUNDANT_INTERFACES\" category=\"STYLE\" --> RedundantInterface\nabbrev=\"DMI\" type=\"DMI_CALLING_NEXT_FROM_HASNEXT\" category=\"CORRECTNESS\" --> IteratorHasNextCallsNext\nabbrev=\"BSHIFT\" type=\"ICAST_BAD_SHIFT_AMOUNT\" category=\"CORRECTNESS\" --> BitShiftInvalidAmount\nabbrev=\"INT\" type=\"INT_BAD_COMPARISON_WITH_INT_VALUE\" category=\"CORRECTNESS\" --> ComparisonWithOutOfRangeValue\nabbrev=\"UC\" type=\"UC_USELESS_CONDITION_TYPE\" category=\"STYLE\" --> ComparisonWithOutOfRangeValue\nabbrev=\"Eq\" type=\"EQ_COMPARING_CLASS_NAMES\" category=\"CORRECTNESS\" --> EqualsClassNames\nabbrev=\"Eq\" type=\"EQ_DONT_DEFINE_EQUALS_FOR_ENUM\" category=\"CORRECTNESS\" --> EqualsEnum\nabbrev=\"Eq\" type=\"EQ_SELF_USE_OBJECT\" category=\"CORRECTNESS\" --> EqualsSelf\nabbrev=\"Eq\" type=\"EQ_SELF_NO_OBJECT\" category=\"BAD_PRACTICE\" --> EqualsSelf\nabbrev=\"Eq\" type=\"EQ_OTHER_USE_OBJECT\" category=\"CORRECTNESS\" --> EqualsOther\nabbrev=\"Eq\" type=\"EQ_OTHER_NO_OBJECT\" category=\"CORRECTNESS\" --> EqualsOther\nabbrev=\"RE\" type=\"RE_POSSIBLE_UNINTENDED_PATTERN\" category=\"CORRECTNESS\" --> RegexUnintended\nabbrev=\"RE\" type=\"RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION\" category=\"CORRECTNESS\" --> RegexBadSyntax\nabbrev=\"RE\" type=\"RE_CANT_USE_FILE_SEPARATOR_AS_REGULAR_EXPRESSION\" category=\"CORRECTNESS\" --> RegexFileSeparator\nabbrev=\"Nm\" type=\"NM_SAME_SIMPLE_NAME_AS_INTERFACE\" category=\"BAD_PRACTICE\" --> BadNameOfClassSameAsInterface\nabbrev=\"Nm\" type=\"NM_SAME_SIMPLE_NAME_AS_SUPERCLASS\" category=\"BAD_PRACTICE\" --> BadNameOfClassSameAsSuperclass\nabbrev=\"INT\" type=\"INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE\" category=\"CORRECTNESS\" --> ComparisonWithOutOfRangeValue\nabbrev=\"INT\" type=\"INT_VACUOUS_COMPARISON\" category=\"STYLE\" --> ComparisonWithOutOfRangeValue\nabbrev=\"IP\" type=\"IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN\" category=\"CORRECTNESS\" cweid=\"563\" --> ParameterOverwritten\nabbrev=\"INT\" type=\"INT_BAD_COMPARISON_WITH_SIGNED_BYTE\" category=\"CORRECTNESS\" --> ComparisonWithOutOfRangeValue, SwitchBranchUnreachable\nabbrev=\"BIT\" type=\"BIT_ADD_OF_SIGNED_BYTE\" category=\"CORRECTNESS\" --> BitAddSignedByte\nabbrev=\"BIT\" type=\"BIT_IOR_OF_SIGNED_BYTE\" category=\"CORRECTNESS\" --> BitOrSignedByte\nabbrev=\"BIT\" type=\"BIT_AND_ZZ\" category=\"CORRECTNESS\" --> UselessAndWithZero *\nabbrev=\"JLM\" type=\"JML_JSR166_CALLING_WAIT_RATHER_THAN_AWAIT\" category=\"MT_CORRECTNESS\" --> IncorrectConcurrentMethod\nabbrev=\"DL\" type=\"DL_SYNCHRONIZATION_ON_BOOLEAN\" category=\"MT_CORRECTNESS\" --> SynchronizationOnBoolean\nabbrev=\"DL\" type=\"DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE\" category=\"MT_CORRECTNESS\" --> SynchronizationOnBoxedNumber\nabbrev=\"DL\" type=\"DL_SYNCHRONIZATION_ON_UNSHARED_BOXED_PRIMITIVE\" category=\"MT_CORRECTNESS\" --> SynchronizationOnUnsharedBoxed\nabbrev=\"ESync\" type=\"ESync_EMPTY_SYNC\" category=\"MT_CORRECTNESS\" cweid=\"585\" --> EmptySynchronizeBlock\nabbrev=\"SBSC\" type=\"SBSC_USE_STRINGBUFFER_CONCATENATION\" category=\"PERFORMANCE\" --> StringConcatInLoop\nabbrev=\"SP\" type=\"SP_SPIN_ON_FIELD\" category=\"MT_CORRECTNESS\" --> SpinLoopOnField\nabbrev=\"SW\" type=\"SW_SWING_METHODS_INVOKED_IN_SWING_THREAD\" category=\"BAD_PRACTICE\" --> SwingMethodNotInSwingThread\nabbrev=\"Dm\" type=\"DM_CONVERT_CASE\" category=\"I18N\" --> ConvertCaseWithDefaultLocale\nabbrev=\"Dm\" type=\"DM_DEFAULT_ENCODING\" category=\"I18N\" --> MethodReliesOnDefaultEncoding\nabbrev=\"IL\" type=\"IL_INFINITE_RECURSIVE_LOOP\" category=\"CORRECTNESS\" cweid=\"674\" --> InfiniteRecursion\nabbrev=\"IL\" type=\"IL_CONTAINER_ADDED_TO_ITSELF\" category=\"CORRECTNESS\" --> CollectionAddedToItself\nabbrev=\"IL\" type=\"IL_INFINITE_LOOP\" category=\"CORRECTNESS\" --> InfiniteLoop, InvariantLoopCondition\nabbrev=\"VA\" type=\"VA_PRIMITIVE_ARRAY_PASSED_TO_OBJECT_VARARG\" category=\"CORRECTNESS\" --> PrimitiveArrayPassedAsVarArg\nabbrev=\"IM\" type=\"IM_BAD_CHECK_FOR_ODD\" category=\"STYLE\" --> CheckForOddnessFailsForNegative\nabbrev=\"IM\" type=\"IM_AVERAGE_COMPUTATION_COULD_OVERFLOW\" category=\"STYLE\" --> AverageComputationCouldOverflow\nabbrev=\"Dm\" type=\"DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION\" category=\"CORRECTNESS\" --> AnnotationNoRuntimeRetention\nabbrev=\"It\" type=\"IT_NO_SUCH_ELEMENT\" category=\"BAD_PRACTICE\" --> IteratorNoThrow\nabbrev=\"DLS\" type=\"DLS_DEAD_LOCAL_STORE_IN_RETURN\" category=\"STYLE\" --> DeadStoreInReturn\nabbrev=\"DLS\" type=\"DLS_DEAD_LOCAL_INCREMENT_IN_RETURN\" category=\"CORRECTNESS\" --> DeadIncrementInReturn\nabbrev=\"CN\" type=\"CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE\" category=\"BAD_PRACTICE\" --> NotCloneableHasClone\nabbrev=\"CN\" type=\"CN_IDIOM\" category=\"BAD_PRACTICE\" --> CloneableDoesNotImplementClone\nabbrev=\"CN\" type=\"CN_IDIOM_NO_SUPER_CALL\" category=\"BAD_PRACTICE\" --> CloneableNoSuperCall\nabbrev=\"WL\" type=\"WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL\" category=\"MT_CORRECTNESS\" --> SyncOnGetClass\nabbrev=\"Dm\" type=\"DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD\" category=\"CORRECTNESS\" --> UselessEasyMockCall\nabbrev=\"RR\" type=\"RR_NOT_CHECKED\" category=\"BAD_PRACTICE\" --> ReturnValueOfRead\nabbrev=\"RR\" type=\"SR_NOT_CHECKED\" category=\"BAD_PRACTICE\" --> ReturnValueOfSkip\nabbrev=\"DLS\" type=\"DLS_OVERWRITTEN_INCREMENT\" category=\"CORRECTNESS\" --> DeadIncrementInAssignment\nabbrev=\"DMI\" type=\"DMI_DOH\" category=\"CORRECTNESS\" --> NullCheckMethodForConstant\nabbrev=\"DMI\" type=\"DMI_ARGUMENTS_WRONG_ORDER\" category=\"CORRECTNESS\" --> WrongArgumentOrder\nabbrev=\"Nm\" type=\"NM_FIELD_NAMING_CONVENTION\" category=\"BAD_PRACTICE\" --> BadNameOfField\nabbrev=\"Nm\" type=\"NM_METHOD_CONSTRUCTOR_CONFUSION\" category=\"CORRECTNESS\" --> BadNameOfMethodSameAsConstructor\nabbrev=\"UPM\" type=\"UPM_UNCALLED_PRIVATE_METHOD\" category=\"PERFORMANCE\" --> UncalledPrivateMethod, UncalledPrivateMethodChain\nabbrev=\"RANGE\" type=\"RANGE_ARRAY_INDEX\" category=\"CORRECTNESS\" --> ArrayIndexOutOfRange, ArrayIndexNegative\nabbrev=\"RANGE\" type=\"RANGE_ARRAY_OFFSET\" category=\"CORRECTNESS\" --> ArrayOffsetOutOfRange, ArrayIndexNegative\nabbrev=\"RANGE\" type=\"RANGE_ARRAY_LENGTH\" category=\"CORRECTNESS\" --> ArrayLengthOutOfRange, ArrayIndexNegative\nabbrev=\"HE\" type=\"HE_HASHCODE_USE_OBJECT_EQUALS\" category=\"BAD_PRACTICE\" --> HashCodeObjectEquals\nabbrev=\"HE\" type=\"HE_HASHCODE_NO_EQUALS\" category=\"BAD_PRACTICE\" --> HashCodeNoEquals\nabbrev=\"HE\" type=\"HE_EQUALS_USE_HASHCODE\" category=\"BAD_PRACTICE\" --> EqualsObjectHashCode\nabbrev=\"HE\" type=\"HE_EQUALS_NO_HASHCODE\" category=\"BAD_PRACTICE\" --> EqualsNoHashCode\nabbrev=\"CAA\" type=\"CAA_COVARIANT_ARRAY_ELEMENT_STORE\" category=\"CORRECTNESS\" experimental=\"true\" --> ContravariantArrayStore\nabbrev=\"DE\" type=\"DE_MIGHT_IGNORE\" category=\"BAD_PRACTICE\" --> IgnoredException\nabbrev=\"SA\" type=\"SA_FIELD_DOUBLE_ASSIGNMENT\" category=\"STYLE\" --> FieldDoubleAssignment\nabbrev=\"UuF\" type=\"UUF_UNUSED_FIELD\" category=\"PERFORMANCE\" --> UnusedPrivateField\nabbrev=\"UuF\" type=\"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD\" category=\"STYLE\" --> UnusedPublicField\nabbrev=\"UrF\" type=\"URF_UNREAD_FIELD\" category=\"PERFORMANCE\" --> UnreadPrivateField\nabbrev=\"UrF\" type=\"URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD\" category=\"STYLE\" --> UnreadPublicField\nabbrev=\"SS\" type=\"SS_SHOULD_BE_STATIC\" category=\"PERFORMANCE\" --> FieldShouldBeStatic\nabbrev=\"NP\" type=\"NP_UNWRITTEN_FIELD\" category=\"CORRECTNESS\" --> UnwrittenPrivateField\nabbrev=\"NP\" type=\"NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD\" category=\"STYLE\" --> UnwrittenPublicField\nabbrev=\"UwF\" type=\"UWF_UNWRITTEN_FIELD\" category=\"CORRECTNESS\" --> UnwrittenPrivateField\nabbrev=\"UwF\" type=\"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD\" category=\"STYLE\" --> UnwrittenPublicField\nabbrev=\"UwF\" type=\"UWF_NULL_FIELD\" category=\"CORRECTNESS\" --> FieldIsAlwaysNull\nabbrev=\"Se\" type=\"SE_NONFINAL_SERIALVERSIONID\" category=\"BAD_PRACTICE\" --> SerialVersionUidNotFinal\nabbrev=\"Se\" type=\"SE_NONSTATIC_SERIALVERSIONID\" category=\"BAD_PRACTICE\" --> SerialVersionUidNotStatic\nabbrev=\"Se\" type=\"SE_NONLONG_SERIALVERSIONID\" category=\"BAD_PRACTICE\" --> SerialVersionUidNotLong\nabbrev=\"VO\" type=\"VO_VOLATILE_REFERENCE_TO_ARRAY\" category=\"MT_CORRECTNESS\" --> VolatileArray\nabbrev=\"MS\" type=\"MS_OOI_PKGPROTECT\" category=\"MALICIOUS_CODE\" --> StaticFieldShouldBeNonInterfacePackagePrivate\nabbrev=\"MS\" type=\"MS_FINAL_PKGPROTECT\" category=\"MALICIOUS_CODE\" --> StaticFieldShouldBeFinalAndPackagePrivate\nabbrev=\"MS\" type=\"MS_SHOULD_BE_FINAL\" category=\"MALICIOUS_CODE\" --> StaticFieldShouldBeFinal\nabbrev=\"MS\" type=\"MS_SHOULD_BE_REFACTORED_TO_BE_FINAL\" category=\"MALICIOUS_CODE\" --> StaticFieldShouldBeRefactoredToFinal\nabbrev=\"MS\" type=\"MS_PKGPROTECT\" category=\"MALICIOUS_CODE\" --> StaticFieldShouldBePackagePrivate\nabbrev=\"MS\" type=\"MS_MUTABLE_ARRAY\" category=\"MALICIOUS_CODE\" --> StaticFieldMutableArray\nabbrev=\"MS\" type=\"MS_CANNOT_BE_FINAL\" category=\"MALICIOUS_CODE\" --> StaticFieldCannotBeFinal\nabbrev=\"MS\" type=\"MS_MUTABLE_HASHTABLE\" category=\"MALICIOUS_CODE\" --> StaticFieldMutableCollection\nabbrev=\"MS\" type=\"MS_MUTABLE_COLLECTION\" category=\"MALICIOUS_CODE\" --> StaticFieldMutableCollection\nabbrev=\"MS\" type=\"MS_MUTABLE_COLLECTION_PKGPROTECT\" category=\"MALICIOUS_CODE\" --> StaticFieldShouldBePackagePrivate\nabbrev=\"MS\" type=\"MS_EXPOSE_REP\" category=\"MALICIOUS_CODE\" --> ExposeMutableStaticFieldViaReturnValue\nabbrev=\"EI\" type=\"EI_EXPOSE_REP\" category=\"MALICIOUS_CODE\" cweid=\"374\" --> ExposeMutableFieldViaReturnValue\nabbrev=\"Co\" type=\"CO_COMPARETO_RESULTS_MIN_VALUE\" category=\"BAD_PRACTICE\" --> CompareReturnsMinValue\nabbrev=\"UMAC\" type=\"UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS\" category=\"CORRECTNESS\" --> UncalledMethodOfAnonymousClass\nabbrev=\"DLS\" type=\"DLS_DEAD_LOCAL_STORE\" category=\"STYLE\" --> DeadLocalStore, DeadParameterStore, UnusedLocalVariable\nabbrev=\"DLS\" type=\"DLS_DEAD_LOCAL_STORE_OF_NULL\" category=\"STYLE\" --> DeadLocalStore, DeadParameterStore, UnusedLocalVariable\nabbrev=\"BSHIFT\" type=\"BSHIFT_WRONG_ADD_PRIORITY\" category=\"CORRECTNESS\" --> BitShiftWrongPriority\nabbrev=\"JCIP\" type=\"JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS\" category=\"BAD_PRACTICE\" --> NonFinalFieldInImmutableClass\nabbrev=\"Se\" type=\"SE_METHOD_MUST_BE_PRIVATE\" category=\"CORRECTNESS\" --> SerializationMethodMustBePrivate\nabbrev=\"UC\" type=\"UC_USELESS_VOID_METHOD\" category=\"STYLE\" --> UselessVoidMethod\nabbrev=\"ME\" type=\"ME_MUTABLE_ENUM_FIELD\" category=\"BAD_PRACTICE\" --> MutableEnumField\nabbrev=\"SQL\" type=\"SQL_BAD_RESULTSET_ACCESS\" category=\"CORRECTNESS\" --> BadResultSetArgument\nabbrev=\"SQL\" type=\"SQL_BAD_PREPARED_STATEMENT_ACCESS\" category=\"CORRECTNESS\" --> BadPreparedStatementArgument\nabbrev=\"Dm\" type=\"DMI_UNSUPPORTED_METHOD\" category=\"STYLE\" --> UnsupportedCall\nabbrev=\"STCAL\" type=\"STCAL_STATIC_CALENDAR_INSTANCE\" category=\"MT_CORRECTNESS\" --> StaticNotThreadSafeField\nabbrev=\"STCAL\" type=\"STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE\" category=\"MT_CORRECTNESS\" --> StaticNotThreadSafeField\nabbrev=\"STCAL\" type=\"STCAL_INVOKE_ON_STATIC_CALENDAR_INSTANCE\" category=\"MT_CORRECTNESS\" --> StaticNotThreadSafeFieldInvoke\nabbrev=\"STCAL\" type=\"STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE\" category=\"MT_CORRECTNESS\" --> StaticNotThreadSafeFieldInvoke\nabbrev=\"Nm\" type=\"NM_FUTURE_KEYWORD_USED_AS_MEMBER_IDENTIFIER\" category=\"BAD_PRACTICE\" --> BadNameOfMethodFutureKeyword, BadNameOfFieldFutureKeyword\nabbrev=\"ML\" type=\"ML_SYNC_ON_UPDATED_FIELD\" category=\"MT_CORRECTNESS\" --> SynchronizationOnUpdatedField\nabbrev=\"MSF\" type=\"MSF_MUTABLE_SERVLET_FIELD\" category=\"MT_CORRECTNESS\" --> MutableServletField\nabbrev=\"Se\" type=\"SE_READ_RESOLVE_IS_STATIC\" category=\"CORRECTNESS\" --> ReadResolveIsStatic\nabbrev=\"Se\" type=\"SE_READ_RESOLVE_MUST_RETURN_OBJECT\" category=\"BAD_PRACTICE\" --> ReadResolveMustReturnObject\nabbrev=\"BC\" type=\"BC_IMPOSSIBLE_DOWNCAST\" category=\"CORRECTNESS\" cweid=\"570\" --> ImpossibleCast\nabbrev=\"BC\" type=\"BC_IMPOSSIBLE_CAST\" category=\"CORRECTNESS\" cweid=\"570\" --> ImpossibleCast\nabbrev=\"BC\" type=\"BC_IMPOSSIBLE_INSTANCEOF\" category=\"CORRECTNESS\" cweid=\"570\" --> ImpossibleInstanceOf\nabbrev=\"BC\" type=\"BC_VACUOUS_INSTANCEOF\" category=\"STYLE\" cweid=\"571\" --> UnnecessaryInstanceOf\nabbrev=\"RCN\" type=\"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE\" category=\"STYLE\" --> RedundantNullCheckNull\nabbrev=\"RCN\" type=\"RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE\" category=\"CORRECTNESS\" --> RedundantNullCheckDeref\nabbrev=\"RCN\" type=\"RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE\" category=\"STYLE\" --> RedundantNullCheck, RedundantNullCheckChecked\nabbrev=\"RCN\" type=\"RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES\" category=\"STYLE\" --> RedundantComparisonNull\nabbrev=\"RCN\" type=\"RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE\" category=\"STYLE\" --> RedundantComparisonNullNonNull\nabbrev=\"NP\" type=\"NP_ALWAYS_NULL\" category=\"CORRECTNESS\" --> NullDereferenceGuaranteed\nabbrev=\"WS\" type=\"WS_WRITEOBJECT_SYNC\" category=\"MT_CORRECTNESS\" --> WriteObjectIsSynchronized\nabbrev=\"RS\" type=\"RS_READOBJECT_SYNC\" category=\"MT_CORRECTNESS\" --> ReadObjectIsSynchronized\n\n==== Not reimplemented\n\nabbrev=\"NP\" type=\"NP_GUARANTEED_DEREF\" category=\"CORRECTNESS\"\nabbrev=\"Ru\" type=\"RU_INVOKE_RUN\" category=\"MT_CORRECTNESS\" cweid=\"572\"\nabbrev=\"DL\" type=\"DL_SYNCHRONIZATION_ON_SHARED_CONSTANT\" category=\"MT_CORRECTNESS\"\nabbrev=\"XSS\" type=\"XSS_REQUEST_PARAMETER_TO_SEND_ERROR\" category=\"SECURITY\" cweid=\"81\"\nabbrev=\"XSS\" type=\"XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER\" category=\"SECURITY\"\nabbrev=\"XSS\" type=\"XSS_REQUEST_PARAMETER_TO_JSP_WRITER\" category=\"SECURITY\"\nabbrev=\"HRS\" type=\"HRS_REQUEST_PARAMETER_TO_HTTP_HEADER\" category=\"SECURITY\" cweid=\"113\"\nabbrev=\"HRS\" type=\"HRS_REQUEST_PARAMETER_TO_COOKIE\" category=\"SECURITY\" cweid=\"113\" \nabbrev=\"PT\" type=\"PT_ABSOLUTE_PATH_TRAVERSAL\" category=\"SECURITY\" cweid=\"36\" \nabbrev=\"PT\" type=\"PT_RELATIVE_PATH_TRAVERSAL\" category=\"SECURITY\" cweid=\"23\" \nabbrev=\"NP\" type=\"NP_SYNC_AND_NULL_CHECK_FIELD\" category=\"MT_CORRECTNESS\" cweid=\"585\"\nabbrev=\"NP\" type=\"NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR\" category=\"CORRECTNESS\"\nabbrev=\"HSC\" type=\"HSC_HUGE_SHARED_STRING_CONSTANT\" category=\"PERFORMANCE\"\nabbrev=\"DP\" type=\"DP_DO_INSIDE_DO_PRIVILEGED\" category=\"MALICIOUS_CODE\"\nabbrev=\"DP\" type=\"DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED\" category=\"MALICIOUS_CODE\"\nabbrev=\"DE\" type=\"DE_MIGHT_DROP\" category=\"BAD_PRACTICE\"\nabbrev=\"Nm\" type=\"NM_FUTURE_KEYWORD_USED_AS_IDENTIFIER\" category=\"BAD_PRACTICE\"\nabbrev=\"Dm\" type=\"DMI_CONSTANT_DB_PASSWORD\" category=\"SECURITY\" cweid=\"259\"\nabbrev=\"Dm\" type=\"DMI_EMPTY_DB_PASSWORD\" category=\"SECURITY\" cweid=\"259\"\nabbrev=\"Dm\" type=\"DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED\" category=\"STYLE\"\nabbrev=\"Dm\" type=\"DMI_COLLECTION_OF_URLS\" category=\"PERFORMANCE\"\nabbrev=\"Bx\" type=\"DM_BOXED_PRIMITIVE_FOR_PARSING\" category=\"PERFORMANCE\"\nabbrev=\"Bx\" type=\"DM_BOXED_PRIMITIVE_FOR_COMPARE\" category=\"PERFORMANCE\"\nabbrev=\"Bx\" type=\"BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR\" category=\"PERFORMANCE\"\nabbrev=\"Dm\" type=\"DM_MONITOR_WAIT_ON_CONDITION\" category=\"MT_CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_ARGUMENT_MIGHT_BE_NULL\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT\" category=\"BAD_PRACTICE\"\nabbrev=\"NP\" type=\"NP_DEREFERENCE_OF_READLINE_VALUE\" category=\"STYLE\"\nabbrev=\"NP\" type=\"NP_IMMEDIATE_DEREFERENCE_OF_READLINE\" category=\"STYLE\"\nabbrev=\"SQL\" type=\"SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE\" category=\"SECURITY\" cweid=\"89\"\nabbrev=\"SQL\" type=\"SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING\" category=\"SECURITY\" cweid=\"89\"\nabbrev=\"DC\" type=\"DC_DOUBLECHECK\" category=\"MT_CORRECTNESS\" cweid=\"609\"\nabbrev=\"DC\" type=\"DC_PARTIALLY_CONSTRUCTED\" category=\"MT_CORRECTNESS\" cweid=\"609\"\nabbrev=\"Eq\" type=\"EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS\" category=\"BAD_PRACTICE\"\nabbrev=\"Eq\" type=\"EQ_UNUSUAL\" category=\"STYLE\"\nabbrev=\"Eq\" type=\"EQ_GETCLASS_AND_CLASS_CONSTANT\" category=\"BAD_PRACTICE\"\nabbrev=\"Eq\" type=\"EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC\" category=\"CORRECTNESS\"\nabbrev=\"Eq\" type=\"EQ_DOESNT_OVERRIDE_EQUALS\" category=\"STYLE\"\nabbrev=\"Co\" type=\"CO_SELF_NO_OBJECT\" category=\"BAD_PRACTICE\"\nabbrev=\"Co\" type=\"CO_COMPARETO_INCORRECT_FLOATING\" category=\"BAD_PRACTICE\"\nabbrev=\"ES\" type=\"ES_COMPARING_STRINGS_WITH_EQ\" category=\"BAD_PRACTICE\"\nabbrev=\"ES\" type=\"ES_COMPARING_PARAMETER_STRING_WITH_EQ\" category=\"BAD_PRACTICE\"\nabbrev=\"HE\" type=\"HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS\" category=\"CORRECTNESS\"\nabbrev=\"HE\" type=\"HE_USE_OF_UNHASHABLE_CLASS\" category=\"CORRECTNESS\"\nabbrev=\"Eq\" type=\"EQ_COMPARETO_USE_OBJECT_EQUALS\" category=\"BAD_PRACTICE\"\nabbrev=\"HE\" type=\"HE_INHERITS_EQUALS_USE_HASHCODE\" category=\"BAD_PRACTICE\"\nabbrev=\"Eq\" type=\"EQ_ABSTRACT_SELF\" category=\"BAD_PRACTICE\"\nabbrev=\"Co\" type=\"CO_ABSTRACT_SELF\" category=\"BAD_PRACTICE\"\nabbrev=\"IS\" type=\"IS2_INCONSISTENT_SYNC\" category=\"MT_CORRECTNESS\"\nabbrev=\"TLW\" type=\"TLW_TWO_LOCK_WAIT\" category=\"MT_CORRECTNESS\"\nabbrev=\"UR\" type=\"UR_UNINIT_READ\" category=\"CORRECTNESS\"\nabbrev=\"UR\" type=\"UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR\" category=\"CORRECTNESS\"\nabbrev=\"UG\" type=\"UG_SYNC_SET_UNSYNC_GET\" category=\"MT_CORRECTNESS\"\nabbrev=\"IC\" type=\"IC_INIT_CIRCULARITY\" category=\"STYLE\"\nabbrev=\"IC\" type=\"IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION\" category=\"BAD_PRACTICE\"\nabbrev=\"SI\" type=\"SI_INSTANCE_BEFORE_FINALS_ASSIGNED\" category=\"BAD_PRACTICE\"\nabbrev=\"IS\" type=\"IS_FIELD_NOT_GUARDED\" category=\"MT_CORRECTNESS\"\nabbrev=\"ML\" type=\"ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD\" category=\"MT_CORRECTNESS\"\nabbrev=\"ME\" type=\"ME_ENUM_FIELD_SETTER\" category=\"BAD_PRACTICE\"\nabbrev=\"Nm\" type=\"NM_VERY_CONFUSING\" category=\"CORRECTNESS\"\nabbrev=\"Nm\" type=\"NM_VERY_CONFUSING_INTENTIONAL\" category=\"BAD_PRACTICE\"\nabbrev=\"Nm\" type=\"NM_WRONG_PACKAGE\" category=\"CORRECTNESS\"\nabbrev=\"Nm\" type=\"NM_WRONG_PACKAGE_INTENTIONAL\" category=\"BAD_PRACTICE\"\nabbrev=\"Nm\" type=\"NM_CONFUSING\" category=\"BAD_PRACTICE\"\nabbrev=\"IA\" type=\"IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD\" category=\"STYLE\"\nabbrev=\"Se\" type=\"SE_PRIVATE_READ_RESOLVE_NOT_INHERITED\" category=\"STYLE\"\nabbrev=\"Se\" type=\"SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS\" category=\"STYLE\"\nabbrev=\"Se\" type=\"SE_NO_SUITABLE_CONSTRUCTOR\" category=\"BAD_PRACTICE\"\nabbrev=\"Se\" type=\"SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION\" category=\"BAD_PRACTICE\"\nabbrev=\"SnVI\" type=\"SE_NO_SERIALVERSIONID\" category=\"BAD_PRACTICE\"\nabbrev=\"Se\" type=\"SE_TRANSIENT_FIELD_NOT_RESTORED\" category=\"BAD_PRACTICE\"\nabbrev=\"Se\" type=\"SE_BAD_FIELD\" category=\"BAD_PRACTICE\"\nabbrev=\"Se\" type=\"SE_INNER_CLASS\" category=\"BAD_PRACTICE\"\nabbrev=\"Se\" type=\"SE_BAD_FIELD_INNER_CLASS\" category=\"BAD_PRACTICE\"\nabbrev=\"Se\" type=\"SE_BAD_FIELD_STORE\" category=\"BAD_PRACTICE\"\nabbrev=\"SF\" type=\"SF_SWITCH_FALLTHROUGH\" category=\"STYLE\" cweid=\"484\"\nabbrev=\"SF\" type=\"SF_SWITCH_NO_DEFAULT\" category=\"STYLE\"\nabbrev=\"SF\" type=\"SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH\" category=\"CORRECTNESS\" cweid=\"484\"\nabbrev=\"SF\" type=\"SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW\" category=\"CORRECTNESS\" cweid=\"484\"\nabbrev=\"UwF\" type=\"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR\" category=\"STYLE\"\nabbrev=\"SIC\" type=\"SIC_THREADLOCAL_DEADLY_EMBRACE\" category=\"CORRECTNESS\"\nabbrev=\"SIC\" type=\"SIC_INNER_SHOULD_BE_STATIC\" category=\"PERFORMANCE\"\nabbrev=\"SIC\" type=\"SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS\" category=\"PERFORMANCE\"\nabbrev=\"SIC\" type=\"SIC_INNER_SHOULD_BE_STATIC_ANON\" category=\"PERFORMANCE\"\nabbrev=\"Wa\" type=\"WA_AWAIT_NOT_IN_LOOP\" category=\"MT_CORRECTNESS\"\nabbrev=\"No\" type=\"NO_NOTIFY_NOT_NOTIFYALL\" category=\"MT_CORRECTNESS\"\nabbrev=\"UC\" type=\"UC_USELESS_CONDITION\" category=\"STYLE\"\nabbrev=\"UC\" type=\"UC_USELESS_OBJECT\" category=\"STYLE\"\nabbrev=\"UC\" type=\"UC_USELESS_OBJECT_STACK\" category=\"STYLE\"\nabbrev=\"RV\" type=\"RV_RETURN_VALUE_IGNORED\" category=\"CORRECTNESS\"\nabbrev=\"RV\" type=\"RV_RETURN_VALUE_IGNORED_INFERRED\" category=\"STYLE\"\nabbrev=\"RV\" type=\"RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT\" category=\"STYLE\"\nabbrev=\"RV\" type=\"RV_RETURN_VALUE_IGNORED_BAD_PRACTICE\" category=\"BAD_PRACTICE\" cweid=\"253\"\nabbrev=\"RV\" type=\"RV_CHECK_FOR_POSITIVE_INDEXOF\" category=\"STYLE\"\nabbrev=\"RV\" type=\"RV_DONT_JUST_NULL_CHECK_READLINE\" category=\"STYLE\"\nabbrev=\"NP\" type=\"NP_CLOSING_NULL\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_STORE_INTO_NONNULL_FIELD\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_ALWAYS_NULL_EXCEPTION\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE\" category=\"STYLE\"\nabbrev=\"NP\" type=\"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE\" category=\"STYLE\"\nabbrev=\"NP\" type=\"NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE\" category=\"STYLE\"\nabbrev=\"NP\" type=\"NP_NULL_ON_SOME_PATH\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_NULL_ON_SOME_PATH_EXCEPTION\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_NULL_PARAM_DEREF\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_NULL_PARAM_DEREF_NONVIRTUAL\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_NONNULL_PARAM_VIOLATION\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_NONNULL_RETURN_VIOLATION\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_TOSTRING_COULD_RETURN_NULL\" category=\"BAD_PRACTICE\"\nabbrev=\"NP\" type=\"NP_CLONE_COULD_RETURN_NULL\" category=\"BAD_PRACTICE\"\nabbrev=\"NP\" type=\"NP_LOAD_OF_KNOWN_NULL_VALUE\" category=\"STYLE\"\nabbrev=\"NP\" type=\"NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH\" category=\"CORRECTNESS\"\nabbrev=\"OS\" type=\"OS_OPEN_STREAM\" category=\"BAD_PRACTICE\"\nabbrev=\"OS\" type=\"OS_OPEN_STREAM_EXCEPTION_PATH\" category=\"BAD_PRACTICE\"\nabbrev=\"UL\" type=\"UL_UNRELEASED_LOCK\" category=\"MT_CORRECTNESS\"\nabbrev=\"UL\" type=\"UL_UNRELEASED_LOCK_EXCEPTION_PATH\" category=\"MT_CORRECTNESS\"\nabbrev=\"RC\" type=\"RC_REF_COMPARISON_BAD_PRACTICE\" category=\"BAD_PRACTICE\"\nabbrev=\"RC\" type=\"RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN\" category=\"BAD_PRACTICE\"\nabbrev=\"RC\" type=\"RC_REF_COMPARISON\" category=\"CORRECTNESS\"\nabbrev=\"FS\" type=\"VA_FORMAT_STRING_USES_NEWLINE\" category=\"BAD_PRACTICE\"\nabbrev=\"FS\" type=\"VA_FORMAT_STRING_BAD_CONVERSION\" category=\"CORRECTNESS\"\nabbrev=\"FS\" type=\"VA_FORMAT_STRING_BAD_CONVERSION_TO_BOOLEAN\" category=\"STYLE\"\nabbrev=\"USELESS_STRING\" type=\"VA_FORMAT_STRING_BAD_CONVERSION_FROM_ARRAY\" category=\"CORRECTNESS\"\nabbrev=\"FS\" type=\"VA_FORMAT_STRING_NO_PREVIOUS_ARGUMENT\" category=\"CORRECTNESS\"\nabbrev=\"FS\" type=\"VA_FORMAT_STRING_BAD_ARGUMENT\" category=\"CORRECTNESS\"\nabbrev=\"FS\" type=\"VA_FORMAT_STRING_MISSING_ARGUMENT\" category=\"CORRECTNESS\"\nabbrev=\"FS\" type=\"VA_FORMAT_STRING_ILLEGAL\" category=\"CORRECTNESS\"\nabbrev=\"FS\" type=\"VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED\" category=\"CORRECTNESS\"\nabbrev=\"FS\" type=\"VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED\" category=\"CORRECTNESS\"\nabbrev=\"EC\" type=\"EC_UNRELATED_TYPES_USING_POINTER_EQUALITY\" category=\"CORRECTNESS\"\nabbrev=\"EC\" type=\"EC_UNRELATED_TYPES\" category=\"CORRECTNESS\"\nabbrev=\"EC\" type=\"EC_ARRAY_AND_NONARRAY\" category=\"CORRECTNESS\"\nabbrev=\"EC\" type=\"EC_NULL_ARG\" category=\"CORRECTNESS\"\nabbrev=\"EC\" type=\"EC_UNRELATED_INTERFACES\" category=\"CORRECTNESS\"\nabbrev=\"EC\" type=\"EC_UNRELATED_CLASS_AND_INTERFACE\" category=\"CORRECTNESS\"\nabbrev=\"MWN\" type=\"MWN_MISMATCHED_WAIT\" category=\"MT_CORRECTNESS\"\nabbrev=\"MWN\" type=\"MWN_MISMATCHED_NOTIFY\" category=\"MT_CORRECTNESS\"\nabbrev=\"SA\" type=\"SA_LOCAL_DOUBLE_ASSIGNMENT\" category=\"STYLE\"\nabbrev=\"LI\" type=\"LI_LAZY_INIT_STATIC\" category=\"MT_CORRECTNESS\" cweid=\"543\"\nabbrev=\"LI\" type=\"LI_LAZY_INIT_UPDATE_STATIC\" category=\"MT_CORRECTNESS\" cweid=\"543\"\nabbrev=\"JLM\" type=\"JLM_JSR166_UTILCONCURRENT_MONITORENTER\" category=\"MT_CORRECTNESS\"\nabbrev=\"JLM\" type=\"JLM_JSR166_LOCK_MONITORENTER\" category=\"MT_CORRECTNESS\"\nabbrev=\"ODR\" type=\"ODR_OPEN_DATABASE_RESOURCE\" category=\"BAD_PRACTICE\"\nabbrev=\"ODR\" type=\"ODR_OPEN_DATABASE_RESOURCE_EXCEPTION_PATH\" category=\"BAD_PRACTICE\"\nabbrev=\"IIL\" type=\"IIL_ELEMENTS_GET_LENGTH_IN_LOOP\" category=\"PERFORMANCE\" experimental=\"true\"\nabbrev=\"IIL\" type=\"IIL_PREPARE_STATEMENT_IN_LOOP\" category=\"PERFORMANCE\" experimental=\"true\"\nabbrev=\"IIL\" type=\"IIL_PATTERN_COMPILE_IN_LOOP\" category=\"PERFORMANCE\" experimental=\"true\"\nabbrev=\"IIL\" type=\"IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT\" category=\"PERFORMANCE\" experimental=\"true\"\nabbrev=\"IJU\" type=\"IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD\" category=\"CORRECTNESS\"\nabbrev=\"IJU\" type=\"IJU_BAD_SUITE_METHOD\" category=\"CORRECTNESS\"\nabbrev=\"IJU\" type=\"IJU_SETUP_NO_SUPER\" category=\"CORRECTNESS\"\nabbrev=\"IJU\" type=\"IJU_TEARDOWN_NO_SUPER\" category=\"CORRECTNESS\"\nabbrev=\"IJU\" type=\"IJU_SUITE_NOT_STATIC\" category=\"CORRECTNESS\"\nabbrev=\"IJU\" type=\"IJU_NO_TESTS\" category=\"CORRECTNESS\"\nabbrev=\"BOA\" type=\"BOA_BADLY_OVERRIDDEN_ADAPTER\" category=\"CORRECTNESS\"\nabbrev=\"SQL\" type=\"SQL_BAD_RESULTSET_ACCESS\" category=\"CORRECTNESS\"\nabbrev=\"SQL\" type=\"SQL_BAD_PREPARED_STATEMENT_ACCESS\" category=\"CORRECTNESS\"\nabbrev=\"EC\" type=\"EC_INCOMPATIBLE_ARRAY_COMPARE\" category=\"CORRECTNESS\"\nabbrev=\"EC\" type=\"EC_BAD_ARRAY_COMPARE\" category=\"CORRECTNESS\"\nabbrev=\"STI\" type=\"STI_INTERRUPTED_ON_CURRENTTHREAD\" category=\"CORRECTNESS\"\nabbrev=\"STI\" type=\"STI_INTERRUPTED_ON_UNKNOWNTHREAD\" category=\"CORRECTNESS\"\nabbrev=\"DLS\" type=\"DLS_DEAD_STORE_OF_CLASS_LITERAL\" category=\"CORRECTNESS\"\nabbrev=\"DLS\" type=\"DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD\" category=\"STYLE\"\nabbrev=\"MF\" type=\"MF_METHOD_MASKS_FIELD\" category=\"CORRECTNESS\"\nabbrev=\"MF\" type=\"MF_CLASS_MASKS_FIELD\" category=\"CORRECTNESS\"\nabbrev=\"ISC\" type=\"ISC_INSTANTIATE_STATIC_CLASS\" category=\"BAD_PRACTICE\"\nabbrev=\"REC\" type=\"REC_CATCH_EXCEPTION\" category=\"STYLE\" cweid=\"396\"\nabbrev=\"UM\" type=\"UM_UNNECESSARY_MATH\" category=\"PERFORMANCE\"\nabbrev=\"MTIA\" type=\"MTIA_SUSPECT_STRUTS_INSTANCE_FIELD\" category=\"STYLE\"\nabbrev=\"MTIA\" type=\"MTIA_SUSPECT_SERVLET_INSTANCE_FIELD\" category=\"STYLE\"\nabbrev=\"PS\" type=\"PS_PUBLIC_SEMAPHORES\" category=\"STYLE\"\nabbrev=\"ICAST\" type=\"ICAST_INT_2_LONG_AS_INSTANT\" category=\"CORRECTNESS\"\nabbrev=\"NP\" type=\"NP_NULL_INSTANCEOF\" category=\"CORRECTNESS\"\nabbrev=\"BC\" type=\"BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS\" category=\"BAD_PRACTICE\"\nabbrev=\"BC\" type=\"BC_BAD_CAST_TO_CONCRETE_COLLECTION\" category=\"STYLE\"\nabbrev=\"BC\" type=\"BC_UNCONFIRMED_CAST\" category=\"STYLE\"\nabbrev=\"BC\" type=\"BC_UNCONFIRMED_CAST_OF_RETURN_VALUE\" category=\"STYLE\"\nabbrev=\"BC\" type=\"BC_BAD_CAST_TO_ABSTRACT_COLLECTION\" category=\"STYLE\"\nabbrev=\"BSHIFT\" type=\"ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT\" category=\"STYLE\"\nabbrev=\"DMI\" type=\"DMI_HARDCODED_ABSOLUTE_FILENAME\" category=\"STYLE\"\nabbrev=\"SWL\" type=\"SWL_SLEEP_WITH_LOCK_HELD\" category=\"MT_CORRECTNESS\"\nabbrev=\"J2EE\" type=\"J2EE_STORE_OF_NON_SERIALIZABLE_OBJECT_INTO_SESSION\" category=\"BAD_PRACTICE\" cweid=\"579\"\nabbrev=\"DMI\" type=\"DMI_NONSERIALIZABLE_OBJECT_WRITTEN\" category=\"STYLE\"\nabbrev=\"IMA\" type=\"IMA_INEFFICIENT_MEMBER_ACCESS\" category=\"PERFORMANCE\" experimental=\"true\"\nabbrev=\"XFB\" type=\"XFB_XML_FACTORY_BYPASS\" category=\"STYLE\"\nabbrev=\"USM\" type=\"USM_USELESS_SUBCLASS_METHOD\" category=\"STYLE\" experimental=\"true\"\nabbrev=\"USM\" type=\"USM_USELESS_ABSTRACT_METHOD\" category=\"STYLE\" experimental=\"true\"\nabbrev=\"CI\" type=\"CI_CONFUSED_INHERITANCE\" category=\"STYLE\"\nabbrev=\"GC\" type=\"GC_UNCHECKED_TYPE_IN_GENERIC_CALL\" category=\"BAD_PRACTICE\"\nabbrev=\"GC\" type=\"GC_UNRELATED_TYPES\" category=\"CORRECTNESS\"\nabbrev=\"PZ\" type=\"PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS\" category=\"BAD_PRACTICE\"\nabbrev=\"DMI\" type=\"DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS\" category=\"BAD_PRACTICE\"\nabbrev=\"DMI\" type=\"DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION\" category=\"BAD_PRACTICE\"\nabbrev=\"DMI\" type=\"DMI_VACUOUS_SELF_COLLECTION_CALL\" category=\"CORRECTNESS\"\nabbrev=\"DMI\" type=\"DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES\" category=\"CORRECTNESS\"\nabbrev=\"TQ\" type=\"TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED\" category=\"CORRECTNESS\"\nabbrev=\"TQ\" type=\"TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS\" category=\"CORRECTNESS\"\nabbrev=\"TQ\" type=\"TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED\" category=\"CORRECTNESS\"\nabbrev=\"TQ\" type=\"TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED\" category=\"CORRECTNESS\"\nabbrev=\"TQ\" type=\"TQ_MAYBE_SOURCE_VALUE_REACHES_ALWAYS_SINK\" category=\"CORRECTNESS\"\nabbrev=\"TQ\" type=\"TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK\" category=\"CORRECTNESS\"\nabbrev=\"TQ\" type=\"TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK\" category=\"STYLE\"\nabbrev=\"TQ\" type=\"TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK\" category=\"STYLE\"\nabbrev=\"OBL\" type=\"OBL_UNSATISFIED_OBLIGATION\" category=\"EXPERIMENTAL\" experimental=\"true\"\nabbrev=\"OBL\" type=\"OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE\" category=\"EXPERIMENTAL\" experimental=\"true\"\nabbrev=\"RV\" type=\"RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED\" category=\"MT_CORRECTNESS\"\nabbrev=\"LG\" type=\"LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE\" category=\"EXPERIMENTAL\"\nabbrev=\"NP\" type=\"NP_METHOD_RETURN_RELAXING_ANNOTATION\" category=\"STYLE\" \nabbrev=\"NP\" type=\"NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION\" category=\"STYLE\" \n\n=== Dubious\nabbrev=\"IM\" type=\"IM_MULTIPLYING_RESULT_OF_IREM\" category=\"CORRECTNESS\"\nabbrev=\"QF\" type=\"QF_QUESTIONABLE_FOR_LOOP\" category=\"STYLE\"\nabbrev=\"QBA\" type=\"QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT\" category=\"CORRECTNESS\" cweid=\"481\"\n\n=== Junk\nabbrev=\"SKIPPED\" type=\"SKIPPED_CLASS_TOO_BIG\" category=\"EXPERIMENTAL\"\nabbrev=\"VR\" type=\"VR_UNRESOLVABLE_REFERENCE\" category=\"CORRECTNESS\" experimental=\"true\"\nabbrev=\"TEST\" type=\"UNKNOWN\" category=\"EXPERIMENTAL\"\nabbrev=\"TEST\" type=\"TESTING\" category=\"EXPERIMENTAL\"\nabbrev=\"TEST\" type=\"TESTING1\" category=\"EXPERIMENTAL\"\nabbrev=\"TEST\" type=\"TESTING2\" category=\"EXPERIMENTAL\"\nabbrev=\"TEST\" type=\"TESTING3\" category=\"EXPERIMENTAL\"\nabbrev=\"CAA\" type=\"CAA_COVARIANT_ARRAY_FIELD\" category=\"STYLE\" experimental=\"true\"\nabbrev=\"CAA\" type=\"CAA_COVARIANT_ARRAY_RETURN\" category=\"STYLE\" experimental=\"true\"\nabbrev=\"CAA\" type=\"CAA_COVARIANT_ARRAY_LOCAL\" category=\"STYLE\" experimental=\"true\"\nabbrev=\"IIO\" type=\"IIO_INEFFICIENT_INDEX_OF\" category=\"PERFORMANCE\" experimental=\"true\"\nabbrev=\"IIO\" type=\"IIO_INEFFICIENT_LAST_INDEX_OF\" category=\"PERFORMANCE\" experimental=\"true\"\nabbrev=\"ITA\" type=\"ITA_INEFFICIENT_TO_ARRAY\" category=\"PERFORMANCE\" experimental=\"true\"\nabbrev=\"BAC\" type=\"BAC_BAD_APPLET_CONSTRUCTOR\" category=\"CORRECTNESS\" experimental=\"true\"\nabbrev=\"CD\" type=\"CD_CIRCULAR_DEPENDENCY\" category=\"STYLE\" experimental=\"true\"\nabbrev=\"FL\" type=\"FL_MATH_USING_FLOAT_PRECISION\" category=\"CORRECTNESS\"\nabbrev=\"Bx\" type=\"DM_FP_NUMBER_CTOR\" category=\"PERFORMANCE\"\nabbrev=\"NOISE\" type=\"NOISE_NULL_DEREFERENCE\" category=\"NOISE\"\nabbrev=\"NOISE\" type=\"NOISE_METHOD_CALL\" category=\"NOISE\"\nabbrev=\"NOISE\" type=\"NOISE_FIELD_REFERENCE\" category=\"NOISE\"\nabbrev=\"NOISE\" type=\"NOISE_OPERATION\" category=\"NOISE\"\nabbrev=\"AM\" type=\"AM_CREATES_EMPTY_ZIP_FILE_ENTRY\" category=\"BAD_PRACTICE\"\nabbrev=\"AM\" type=\"AM_CREATES_EMPTY_JAR_FILE_ENTRY\" category=\"BAD_PRACTICE\"\nabbrev=\"Dm\" type=\"DMI_SCHEDULED_THREAD_POOL_EXECUTOR_WITH_ZERO_CORE_THREADS\" category=\"CORRECTNESS\"\nabbrev=\"FB\" type=\"FB_UNEXPECTED_WARNING\" category=\"CORRECTNESS\"\nabbrev=\"FB\" type=\"FB_MISSING_EXPECTED_WARNING\" category=\"CORRECTNESS\"\n"
  },
  {
    "path": "huntbugs/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry excluding=\"**\" kind=\"src\" output=\"target/classes\" path=\"src/main/resources\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "huntbugs/.gitignore",
    "content": "/target/\n/huntbugs*.txt\n/huntbugs*.xml\n/huntbugs*.html\n/run.bat\n/procyon-decompiler-*.jar\n/dependency-reduced-pom.xml\n/dep\n/projects\n"
  },
  {
    "path": "huntbugs/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>huntbugs</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "huntbugs/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/main/resources=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "huntbugs/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.assertIdentifier=error\norg.eclipse.jdt.core.compiler.problem.enumIdentifier=error\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\norg.eclipse.jdt.core.formatter.align_type_members_on_columns=false\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=20\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_assignment=0\norg.eclipse.jdt.core.formatter.alignment_for_binary_expression=20\norg.eclipse.jdt.core.formatter.alignment_for_compact_if=16\norg.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80\norg.eclipse.jdt.core.formatter.alignment_for_enum_constants=0\norg.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16\norg.eclipse.jdt.core.formatter.alignment_for_method_declaration=0\norg.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16\norg.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80\norg.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16\norg.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16\norg.eclipse.jdt.core.formatter.blank_lines_after_imports=1\norg.eclipse.jdt.core.formatter.blank_lines_after_package=1\norg.eclipse.jdt.core.formatter.blank_lines_before_field=0\norg.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0\norg.eclipse.jdt.core.formatter.blank_lines_before_imports=1\norg.eclipse.jdt.core.formatter.blank_lines_before_member_type=1\norg.eclipse.jdt.core.formatter.blank_lines_before_method=1\norg.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1\norg.eclipse.jdt.core.formatter.blank_lines_before_package=0\norg.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1\norg.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1\norg.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false\norg.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false\norg.eclipse.jdt.core.formatter.comment.format_block_comments=true\norg.eclipse.jdt.core.formatter.comment.format_header=false\norg.eclipse.jdt.core.formatter.comment.format_html=true\norg.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true\norg.eclipse.jdt.core.formatter.comment.format_line_comments=false\norg.eclipse.jdt.core.formatter.comment.format_source_code=false\norg.eclipse.jdt.core.formatter.comment.indent_parameter_description=false\norg.eclipse.jdt.core.formatter.comment.indent_root_tags=true\norg.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert\norg.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert\norg.eclipse.jdt.core.formatter.comment.line_length=80\norg.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true\norg.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true\norg.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false\norg.eclipse.jdt.core.formatter.compact_else_if=true\norg.eclipse.jdt.core.formatter.continuation_indentation=2\norg.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2\norg.eclipse.jdt.core.formatter.disabling_tag=@formatter\\:off\norg.eclipse.jdt.core.formatter.enabling_tag=@formatter\\:on\norg.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false\norg.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true\norg.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true\norg.eclipse.jdt.core.formatter.indent_empty_lines=false\norg.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true\norg.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true\norg.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true\norg.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false\norg.eclipse.jdt.core.formatter.indentation.size=4\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert\norg.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert\norg.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert\norg.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert\norg.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert\norg.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert\norg.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert\norg.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.join_lines_in_comments=true\norg.eclipse.jdt.core.formatter.join_wrapped_lines=true\norg.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false\norg.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false\norg.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false\norg.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false\norg.eclipse.jdt.core.formatter.lineSplit=120\norg.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false\norg.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false\norg.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0\norg.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1\norg.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true\norg.eclipse.jdt.core.formatter.tabulation.char=space\norg.eclipse.jdt.core.formatter.tabulation.size=4\norg.eclipse.jdt.core.formatter.use_on_off_tags=false\norg.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false\norg.eclipse.jdt.core.formatter.wrap_before_binary_operator=true\norg.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true\norg.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=false\n"
  },
  {
    "path": "huntbugs/.settings/org.eclipse.jdt.ui.prefs",
    "content": "eclipse.preferences.version=1\neditor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true\nformatter_profile=_StreamEx\nformatter_settings_version=12\norg.eclipse.jdt.ui.javadoc=true\norg.eclipse.jdt.ui.text.custom_code_templates=<?xml version\\=\"1.0\" encoding\\=\"UTF-8\" standalone\\=\"no\"?><templates><template autoinsert\\=\"true\" context\\=\"gettercomment_context\" deleted\\=\"false\" description\\=\"Comment for getter method\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.gettercomment\" name\\=\"gettercomment\">/**\\r\\n * @return the ${bare_field_name}\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"settercomment_context\" deleted\\=\"false\" description\\=\"Comment for setter method\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.settercomment\" name\\=\"settercomment\">/**\\r\\n * @param ${param} the ${bare_field_name} to set\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"constructorcomment_context\" deleted\\=\"false\" description\\=\"Comment for created constructors\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.constructorcomment\" name\\=\"constructorcomment\">/**\\r\\n * ${tags}\\r\\n */</template><template autoinsert\\=\"false\" context\\=\"filecomment_context\" deleted\\=\"false\" description\\=\"Comment for created Java files\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.filecomment\" name\\=\"filecomment\">/*\\r\\n * Copyright 2016 HuntBugs contributors\\r\\n * \\r\\n * Licensed under the Apache License, Version 2.0 (the \"License\");\\r\\n * you may not use this file except in compliance with the License.\\r\\n * You may obtain a copy of the License at\\r\\n * \\r\\n *     http\\://www.apache.org/licenses/LICENSE-2.0\\r\\n * \\r\\n * Unless required by applicable law or agreed to in writing, software\\r\\n * distributed under the License is distributed on an \"AS IS\" BASIS,\\r\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\r\\n * See the License for the specific language governing permissions and\\r\\n * limitations under the License.\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"typecomment_context\" deleted\\=\"false\" description\\=\"Comment for created types\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.typecomment\" name\\=\"typecomment\">/**\\r\\n * @author ${user}\\r\\n *\\r\\n * ${tags}\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"fieldcomment_context\" deleted\\=\"false\" description\\=\"Comment for fields\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.fieldcomment\" name\\=\"fieldcomment\">/**\\r\\n * \\r\\n */</template><template autoinsert\\=\"true\" context\\=\"methodcomment_context\" deleted\\=\"false\" description\\=\"Comment for non-overriding methods\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.methodcomment\" name\\=\"methodcomment\">/**\\r\\n * ${tags}\\r\\n */</template><template autoinsert\\=\"false\" context\\=\"overridecomment_context\" deleted\\=\"false\" description\\=\"Comment for overriding methods\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.overridecomment\" name\\=\"overridecomment\"/><template autoinsert\\=\"true\" context\\=\"delegatecomment_context\" deleted\\=\"false\" description\\=\"Comment for delegate methods\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.delegatecomment\" name\\=\"delegatecomment\">/**\\r\\n * ${tags}\\r\\n * ${see_to_target}\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"newtype_context\" deleted\\=\"false\" description\\=\"Newly created files\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.newtype\" name\\=\"newtype\">${filecomment}\\r\\n${package_declaration}\\r\\n\\r\\n${typecomment}\\r\\n${type_declaration}</template><template autoinsert\\=\"true\" context\\=\"classbody_context\" deleted\\=\"false\" description\\=\"Code in new class type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.classbody\" name\\=\"classbody\">\\r\\n</template><template autoinsert\\=\"true\" context\\=\"interfacebody_context\" deleted\\=\"false\" description\\=\"Code in new interface type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.interfacebody\" name\\=\"interfacebody\">\\r\\n</template><template autoinsert\\=\"true\" context\\=\"enumbody_context\" deleted\\=\"false\" description\\=\"Code in new enum type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.enumbody\" name\\=\"enumbody\">\\r\\n</template><template autoinsert\\=\"true\" context\\=\"annotationbody_context\" deleted\\=\"false\" description\\=\"Code in new annotation type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.annotationbody\" name\\=\"annotationbody\">\\r\\n</template><template autoinsert\\=\"true\" context\\=\"catchblock_context\" deleted\\=\"false\" description\\=\"Code in new catch blocks\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.catchblock\" name\\=\"catchblock\">// ${todo} Auto-generated catch block\\r\\n${exception_var}.printStackTrace();</template><template autoinsert\\=\"true\" context\\=\"methodbody_context\" deleted\\=\"false\" description\\=\"Code in created method stubs\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.methodbody\" name\\=\"methodbody\">// ${todo} Auto-generated method stub\\r\\n${body_statement}</template><template autoinsert\\=\"true\" context\\=\"constructorbody_context\" deleted\\=\"false\" description\\=\"Code in created constructor stubs\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.constructorbody\" name\\=\"constructorbody\">${body_statement}\\r\\n// ${todo} Auto-generated constructor stub</template><template autoinsert\\=\"true\" context\\=\"getterbody_context\" deleted\\=\"false\" description\\=\"Code in created getters\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.getterbody\" name\\=\"getterbody\">return ${field};</template><template autoinsert\\=\"true\" context\\=\"setterbody_context\" deleted\\=\"false\" description\\=\"Code in created setters\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.setterbody\" name\\=\"setterbody\">${field} \\= ${param};</template></templates>\nsp_cleanup.add_default_serial_version_id=true\nsp_cleanup.add_generated_serial_version_id=false\nsp_cleanup.add_missing_annotations=true\nsp_cleanup.add_missing_deprecated_annotations=true\nsp_cleanup.add_missing_methods=false\nsp_cleanup.add_missing_nls_tags=false\nsp_cleanup.add_missing_override_annotations=true\nsp_cleanup.add_missing_override_annotations_interface_methods=true\nsp_cleanup.add_serial_version_id=false\nsp_cleanup.always_use_blocks=true\nsp_cleanup.always_use_parentheses_in_expressions=false\nsp_cleanup.always_use_this_for_non_static_field_access=false\nsp_cleanup.always_use_this_for_non_static_method_access=false\nsp_cleanup.convert_functional_interfaces=false\nsp_cleanup.convert_to_enhanced_for_loop=false\nsp_cleanup.correct_indentation=false\nsp_cleanup.format_source_code=false\nsp_cleanup.format_source_code_changes_only=false\nsp_cleanup.insert_inferred_type_arguments=false\nsp_cleanup.make_local_variable_final=false\nsp_cleanup.make_parameters_final=false\nsp_cleanup.make_private_fields_final=true\nsp_cleanup.make_type_abstract_if_missing_method=false\nsp_cleanup.make_variable_declarations_final=true\nsp_cleanup.never_use_blocks=false\nsp_cleanup.never_use_parentheses_in_expressions=true\nsp_cleanup.on_save_use_additional_actions=true\nsp_cleanup.organize_imports=false\nsp_cleanup.qualify_static_field_accesses_with_declaring_class=false\nsp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true\nsp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true\nsp_cleanup.qualify_static_member_accesses_with_declaring_class=false\nsp_cleanup.qualify_static_method_accesses_with_declaring_class=false\nsp_cleanup.remove_private_constructors=true\nsp_cleanup.remove_redundant_type_arguments=true\nsp_cleanup.remove_trailing_whitespaces=false\nsp_cleanup.remove_trailing_whitespaces_all=true\nsp_cleanup.remove_trailing_whitespaces_ignore_empty=false\nsp_cleanup.remove_unnecessary_casts=false\nsp_cleanup.remove_unnecessary_nls_tags=false\nsp_cleanup.remove_unused_imports=true\nsp_cleanup.remove_unused_local_variables=false\nsp_cleanup.remove_unused_private_fields=true\nsp_cleanup.remove_unused_private_members=false\nsp_cleanup.remove_unused_private_methods=true\nsp_cleanup.remove_unused_private_types=true\nsp_cleanup.sort_members=false\nsp_cleanup.sort_members_all=false\nsp_cleanup.use_anonymous_class_creation=false\nsp_cleanup.use_blocks=false\nsp_cleanup.use_blocks_only_for_return_and_throw=false\nsp_cleanup.use_lambda=true\nsp_cleanup.use_parentheses_in_expressions=false\nsp_cleanup.use_this_for_non_static_field_access=false\nsp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true\nsp_cleanup.use_this_for_non_static_method_access=false\nsp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true\nsp_cleanup.use_type_arguments=false\n"
  },
  {
    "path": "huntbugs/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "huntbugs/.settings/org.eclipse.pde.core.prefs",
    "content": "BUNDLE_ROOT_PATH=target/classes\neclipse.preferences.version=1\n"
  },
  {
    "path": "huntbugs/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n\n <parent>\n  <groupId>one.util</groupId>\n  <artifactId>huntbugs-all</artifactId>\n  <version>0.0.12-SNAPSHOT</version>\n </parent>\n <artifactId>huntbugs</artifactId>\n <packaging>jar</packaging>\n\n <name>huntbugs</name>\n <description>Java bytecode static analysis tool</description>\n\n <properties>\n  <project.mainClass>one.util.huntbugs.HuntBugs</project.mainClass>\n </properties>\n\n <dependencies>\n  <dependency>\n   <groupId>org.bitbucket.mstrobel</groupId>\n   <artifactId>procyon-compilertools</artifactId>\n   <version>0.5.32</version>\n  </dependency>\n  <dependency>\n   <groupId>junit</groupId>\n   <artifactId>junit</artifactId>\n   <version>4.12</version>\n   <scope>test</scope>\n  </dependency>\n  <dependency>\n   <groupId>org.easymock</groupId>\n   <artifactId>easymock</artifactId>\n   <version>3.4</version>\n   <scope>test</scope>\n  </dependency>\n  <dependency>\n   <groupId>net.jcip</groupId>\n   <artifactId>jcip-annotations</artifactId>\n   <version>1.0</version>\n   <scope>test</scope>\n  </dependency>\n  <dependency>\n   <groupId>javax.servlet</groupId>\n   <artifactId>servlet-api</artifactId>\n   <version>2.5</version>\n   <scope>test</scope>\n  </dependency>\n </dependencies>\n\n <build>\n  <plugins>\n   <plugin>\n    <groupId>org.apache.maven.plugins</groupId>\n    <artifactId>maven-jar-plugin</artifactId>\n    <version>3.0.2</version>\n    <configuration>\n     <archive>\n      <manifest>\n       <mainClass>${project.mainClass}</mainClass>\n      </manifest>\n     </archive>\n    </configuration>\n   </plugin>\n   <plugin>\n     <groupId>org.codehaus.mojo</groupId>\n     <artifactId>exec-maven-plugin</artifactId>\n     <version>1.5.0</version>\n     <configuration>\n       <mainClass>${project.mainClass}</mainClass>\n     </configuration>\n   </plugin>\n   <plugin>\n    <groupId>org.jacoco</groupId>\n    <artifactId>jacoco-maven-plugin</artifactId>\n    <version>0.7.9</version>\n    <executions>\n     <execution>\n      <id>default-prepare-agent</id>\n      <goals>\n       <goal>prepare-agent</goal>\n      </goals>\n     </execution>\n     <execution>\n      <id>default-report</id>\n      <phase>prepare-package</phase>\n      <goals>\n       <goal>report</goal>\n      </goals>\n     </execution>\n     <execution>\n      <id>default-check</id>\n      <goals>\n       <goal>check</goal>\n      </goals>\n      <configuration>\n       <rules>\n        <rule implementation=\"org.jacoco.maven.RuleConfiguration\">\n         <element>BUNDLE</element>\n         <limits>\n          <limit implementation=\"org.jacoco.report.check.Limit\">\n           <counter>LINE</counter>\n           <value>COVEREDRATIO</value>\n           <minimum>0.80</minimum>\n          </limit>\n         </limits>\n        </rule>\n       </rules>\n      </configuration>\n     </execution>\n    </executions>\n   </plugin>\n   <plugin>\n    <groupId>org.eluder.coveralls</groupId>\n    <artifactId>coveralls-maven-plugin</artifactId>\n    <version>4.0.0</version>\n   </plugin>\n   <plugin>\n    <artifactId>maven-assembly-plugin</artifactId>\n    <version>2.6</version>\n    <configuration>\n     <descriptorRefs>\n      <descriptorRef>jar-with-dependencies</descriptorRef>\n     </descriptorRefs>\n    </configuration>\n   </plugin>\n  </plugins>\n </build>\n</project>\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/HuntBugs.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.io.UncheckedIOException;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.PathMatcher;\nimport java.nio.file.Paths;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.jar.JarFile;\nimport java.util.logging.LogManager;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport com.strobel.assembler.metadata.ClasspathTypeLoader;\nimport com.strobel.assembler.metadata.CompositeTypeLoader;\nimport com.strobel.assembler.metadata.ITypeLoader;\nimport com.strobel.assembler.metadata.JarTypeLoader;\n\nimport one.util.huntbugs.analysis.AnalysisOptions;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.HuntBugsResult;\nimport one.util.huntbugs.input.XmlReportReader;\nimport one.util.huntbugs.output.Reports;\nimport one.util.huntbugs.repo.AuxRepository;\nimport one.util.huntbugs.repo.CompositeRepository;\nimport one.util.huntbugs.repo.DirRepository;\nimport one.util.huntbugs.repo.JarRepository;\nimport one.util.huntbugs.repo.Repository;\nimport one.util.huntbugs.warning.rule.CategoryRule;\nimport one.util.huntbugs.warning.rule.CompositeRule;\nimport one.util.huntbugs.warning.rule.RegexRule;\nimport one.util.huntbugs.warning.rule.Rule;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class HuntBugs {\n    private boolean listDetectors = false;\n    private boolean listVariables = false;\n    private boolean listDatabases = false;\n    private boolean listMessages = false;\n    private final AnalysisOptions options = new AnalysisOptions();\n    private Repository repo;\n    private Path compareTo;\n\n    private void parseCommandLine(String[] args) {\n        List<Repository> repos = new ArrayList<>();\n        List<ITypeLoader> deps = new ArrayList<>();\n        List<Rule> rules = new ArrayList<>();\n        for (String arg : args) {\n            if (arg.equals(\"-lw\")) {\n                listDetectors = true;\n            } else if (arg.equals(\"-lv\")) {\n                listVariables = true;\n            } else if (arg.equals(\"-ldb\")) {\n                listDatabases = true;\n            } else if (arg.equals(\"-lm\")) {\n                listMessages = true;\n            } else if (arg.startsWith(\"-C\")) {\n                compareTo = Paths.get(arg.substring(2));\n            } else if (arg.startsWith(\"-D\")) {\n                int pos = arg.indexOf('=');\n                if (pos < 0) {\n                    throw new IllegalArgumentException(\"Illegal option: \" + arg + \" (expected -Dname=value)\");\n                }\n                String name = arg.substring(2, pos).trim();\n                String value = arg.substring(pos + 1).trim();\n                options.set(name, value);\n            } else if (arg.startsWith(\"-R\")) {\n                int colonPos = arg.indexOf(':');\n                int equalPos = arg.lastIndexOf('=');\n                if (colonPos < 0 || equalPos < 0 || equalPos < colonPos) {\n                    throw new IllegalArgumentException(\"Illegal option: \" + arg\n                        + \" (expected -Rruletype:rule=adjustment)\");\n                }\n                String ruleType = arg.substring(2, colonPos);\n                String ruleArg = arg.substring(colonPos + 1, equalPos);\n                String adjustmentString = arg.substring(equalPos + 1);\n                int adjustment;\n                if (adjustmentString.equals(\"disable\")) {\n                    adjustment = -100;\n                } else {\n                    try {\n                        adjustment = Integer.parseInt(adjustmentString);\n                    } catch (NumberFormatException e) {\n                        throw new IllegalArgumentException(\"Illegal option: \" + arg\n                            + \": adjustment must be either number from -100 to +100 or 'disable' word\");\n                    }\n                }\n                switch (ruleType) {\n                case \"category\":\n                    rules.add(new CategoryRule(ruleArg, adjustment));\n                    break;\n                case \"pattern\":\n                    rules.add(new RegexRule(ruleArg, adjustment));\n                    break;\n                default:\n                    throw new IllegalArgumentException(\"Illegal option: \" + arg\n                        + \": ruletype must be either 'category' or 'pattern'\");\n                }\n            } else if(arg.startsWith(\"-A\")){\n                try {\n                    glob(arg.substring(2)).map(this::createTypeLoader).forEach(deps::add);\n                } catch (IOException e) {\n                    throw new IllegalArgumentException(\"Cannot open JAR file \" + arg);\n                }\n            } else {\n                try {\n                    glob(arg).map(this::createRepository).forEach(repos::add);\n                } catch (IOException e) {\n                    throw new IllegalArgumentException(\"Cannot open JAR file \" + arg);\n                }\n            }\n        }\n        if (!deps.isEmpty()) {\n            repos.add(new AuxRepository(new CompositeTypeLoader(deps.toArray(new ITypeLoader[0]))));\n        }\n        if (!repos.isEmpty()) {\n            repo = new CompositeRepository(repos);\n        }\n        if (rules.size() == 1)\n            options.setRule(rules.get(0));\n        else if (rules.size() > 1)\n            options.setRule(new CompositeRule(rules));\n    }\n\n    private Repository createRepository(Path path) {\n        try {\n            return Files.isDirectory(path) ? new DirRepository(path) : new JarRepository(new JarFile(path.toFile()));\n        } catch (IOException e) {\n            throw new UncheckedIOException(e);\n        }\n    }\n\n    private ITypeLoader createTypeLoader(Path path) {\n        try {\n            return Files.isDirectory(path) ? new ClasspathTypeLoader(path.toString()) : new JarTypeLoader(new JarFile(path.toFile()));\n        } catch (IOException e) {\n            throw new UncheckedIOException(e);\n        }\n    }\n    \n    private int run(String[] args) {\n        LogManager.getLogManager().reset();\n        if (args.length == 0) {\n            System.out.println(\"Welcome to HuntBugs\");\n            System.out.println(\"Please specify at least one option or at least one directory/jar to analyze\");\n            System.out.println(\"Options are:\");\n            System.out.println(\"    -lw                        -- list all warning types\");\n            System.out.println(\"    -lv                        -- list all variables\");\n            System.out.println(\"    -ldb                       -- list all databases\");\n            System.out.println(\"    -lm                        -- list warning titles\");\n            System.out.println(\"    -ColdResult.xml            -- output difference with old result\");\n            System.out.println(\"    -Apath                     -- dependency path\");\n            System.out.println(\"    -Dname=value               -- set given variable\");\n            System.out.println(\"    -Rruletype:rule=adjustment -- adjust score for warnings\");\n            return -1;\n        }\n        try {\n            parseCommandLine(args);\n        } catch (IllegalArgumentException ex) {\n            System.err.println(ex.getMessage());\n            return -3;\n        }\n        boolean list = false;\n        Context ctx = new Context(repo, options);\n        if (listDetectors) {\n            System.out.println(\"List of warning types:\");\n            ctx.reportWarningTypes(System.out);\n            list = true;\n        }\n        if (listVariables) {\n            System.out.println(\"List of variables:\");\n            options.report(System.out);\n            list = true;\n        }\n        if (listDatabases) {\n            System.out.println(\"List of databases:\");\n            ctx.reportDatabases(System.out);\n            list = true;\n        }\n        if (listMessages) {\n            System.out.println(\"List of warning titles:\");\n            ctx.reportTitles(System.out);\n            list = true;\n        }\n        if (repo == null) {\n            if (list) {\n                ctx.reportStats(System.out);\n                return 0;\n            }\n            System.err.println(\"No repositories specified\");\n            return -2;\n        }\n        long start = System.nanoTime();\n        ctx.addListener((stage, className, count, total) -> {\n            if (count == 0)\n                System.out.printf(\"\\r%70s\\r%s...%n\", \"\", stage);\n            else {\n                if (className == null)\n                    className = \"\";\n                String name = className.length() > 50 ? \"...\" + className.substring(className.length() - 47) : className;\n                System.out.printf(\"\\r%70s\\r[%d/%d] %s\", \"\", count, total, name);\n            }\n            return true;\n        });\n        Runtime.getRuntime().addShutdownHook(\n            new Thread(() -> {\n                try {\n                    ctx.reportErrors(new PrintStream(\"huntbugs.errors.txt\", \"UTF-8\"));\n                    ctx.reportStats(new PrintStream(\"huntbugs.stats.txt\", \"UTF-8\"));\n                    HuntBugsResult result = ctx;\n                    if(compareTo != null) {\n                        try {\n                            result = Reports.diff(XmlReportReader.read(ctx, compareTo), ctx);\n                        } catch (Exception e) {\n                            System.out.println(\"Warning: unable to read old result file \"+compareTo+\": \"+e);\n                            System.out.println(\"Saving non-diff result\");\n                        }\n                    }\n                    Reports.write(Paths.get(\"huntbugs.warnings.xml\"), Paths.get(\"huntbugs.warnings.html\"), result);\n                } catch (IOException e) {\n                    throw new UncheckedIOException(e);\n                }\n                long end = System.nanoTime();\n                Duration dur = Duration.ofNanos(end - start);\n                System.out.printf(\"\\r%70s\\r\", \"\");\n                System.out.println(\"Analyzed \" + ctx.getClassesCount() + \" of \" + ctx.getTotalClasses() + \" classes\");\n                ctx.reportStats(System.out);\n                System.out.println(\"Analyzis time \" + dur.toMinutes() + \"m\" + dur.getSeconds() % 60 + \"s\");\n            }));\n        ctx.analyzePackage(\"\");\n        return 0;\n    }\n    \n    static Stream<Path> glob(String mask) throws IOException {\n        Matcher matcher = Pattern.compile(\"(.*)[\\\\\\\\/](.*)\").matcher(mask);\n        Path parentPath;\n        String fName;\n        if(matcher.matches()) {\n            parentPath = Paths.get(matcher.group(1));\n            fName = matcher.group(2);\n        } else {\n            parentPath = Paths.get(\".\");\n            fName = mask;\n        }\n        PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(\"glob:\" + fName);\n        return Files.list(parentPath).filter(p -> pathMatcher.matches(p.getFileName()));\n    }\n\n    public static void main(String[] args) {\n        System.exit(new HuntBugs().run(args));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/analysis/AnalysisListener.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.analysis;\n\n/**\n * @author Tagir Valeev\n *\n */\n@FunctionalInterface\npublic interface AnalysisListener {\n    /**\n     * @param stepName\n     * @param className\n     * @param count number of classes processed\n     * @param total number of classes to process\n     * @return false if cancel is requested\n     */\n    public boolean eventOccurred(String stepName, String className, int count, int total);\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/analysis/AnalysisOptions.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.analysis;\n\nimport java.io.PrintStream;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.Objects;\n\nimport one.util.huntbugs.warning.rule.Rule;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class AnalysisOptions {\n    public boolean addBootClassPath = true;\n    public int maxMethodSize = 8000;\n    public int classesPerFlush = 1000;\n    public int minScore = 1;\n    private Rule rule = Rule.NULL;\n\n    public void set(String name, String valueString) {\n        Objects.requireNonNull(valueString);\n        try {\n            Field field = getClass().getField(name);\n            Class<?> type = field.getType();\n            Object value;\n            if (type == int.class) {\n                try {\n                    value = Integer.valueOf(valueString);\n                } catch (NumberFormatException e) {\n                    throw new IllegalArgumentException(\"Invalid value \" + valueString + \" for option \" + name\n                        + \" (integer expected)\");\n                }\n            } else if (type == boolean.class) {\n                value = Boolean.valueOf(valueString);\n            } else if (type == String.class) {\n                value = valueString;\n            } else\n                throw new InternalError(\"Unexpected field type: \" + type);\n            field.set(this, value);\n        } catch (NoSuchFieldException e) {\n            throw new IllegalArgumentException(\"Unknown option: \" + name);\n        } catch (SecurityException | IllegalAccessException e) {\n            throw new InternalError(e);\n        }\n    }\n\n    public Rule getRule() {\n        return rule;\n    }\n\n    public void setRule(Rule rule) {\n        this.rule = rule;\n    }\n\n    public void report(PrintStream out) {\n        for(Field field : getClass().getFields()) {\n            if(!Modifier.isPublic(field.getModifiers()))\n                continue;\n            String type = field.getType().getSimpleName();\n            try {\n                out.println(field.getName()+\" (\"+type+\") = \"+field.get(this));\n            } catch (IllegalArgumentException | IllegalAccessException e) {\n                throw new InternalError(e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/analysis/Context.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.analysis;\n\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.stream.Stream;\n\nimport com.strobel.assembler.ir.ConstantPool;\nimport com.strobel.assembler.ir.ConstantPool.TypeInfoEntry;\nimport com.strobel.assembler.metadata.ClasspathTypeLoader;\nimport com.strobel.assembler.metadata.CompositeTypeLoader;\nimport com.strobel.assembler.metadata.ITypeLoader;\nimport com.strobel.assembler.metadata.MetadataSystem;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.registry.DetectorRegistry;\nimport one.util.huntbugs.repo.Repository;\nimport one.util.huntbugs.repo.RepositoryVisitor;\nimport one.util.huntbugs.warning.Messages;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningType;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class Context implements HuntBugsResult {\n    private final List<ErrorMessage> errors = Collections.synchronizedList(new ArrayList<>());\n    private final List<Warning> warnings = Collections.synchronizedList(new ArrayList<>());\n    private final Set<String> missingClasses = ConcurrentHashMap.newKeySet();\n    private final Set<String> classes = ConcurrentHashMap.newKeySet();\n    private final DetectorRegistry registry;\n    private final Repository repository;\n    private final AtomicInteger classesCount = new AtomicInteger();\n    private int totalClasses = 0;\n    private final AnalysisOptions options;\n    private final List<AnalysisListener> listeners = new CopyOnWriteArrayList<>();\n    private final Map<String, Long> stat = new ConcurrentHashMap<>();\n    private Messages msgs;\n    private final ITypeLoader loader;\n\n    public Context(Repository repository, AnalysisOptions options) {\n        this.options = options;\n        registry = new DetectorRegistry(this);\n        this.repository = repository == null ? Repository.createNullRepository() : repository;\n        ITypeLoader loader = this.repository.createTypeLoader();\n        if (options.addBootClassPath) {\n            loader = new CompositeTypeLoader(new ClasspathTypeLoader(System.getProperty(\"sun.boot.class.path\")), loader);\n        }\n        this.loader = loader;\n    }\n    \n    @Override\n    public Messages getMessages() {\n        if(msgs == null) {\n            msgs = Messages.load();\n        }\n        return msgs;\n    }\n\n    public AnalysisOptions getOptions() {\n        return options;\n    }\n\n    public void addListener(AnalysisListener listener) {\n        listeners.add(listener);\n    }\n\n    boolean fireEvent(String stepName, String className, int step, int total) {\n        for (AnalysisListener listener : listeners) {\n            if (!listener.eventOccurred(stepName, className, step, total))\n                return false;\n        }\n        return true;\n    }\n\n    public void analyzePackage(String name) {\n        if (!fireEvent(\"Preparing\", null, 0, 0))\n            return;\n        Set<String> classes = new TreeSet<>();\n        repository.visit(name, new RepositoryVisitor() {\n            @Override\n            public boolean visitPackage(String packageName) {\n                return true;\n            }\n\n            @Override\n            public void visitClass(String className) {\n                classes.add(className);\n            }\n        });\n        totalClasses = classes.size();\n        if(registry.hasDatabases()) {\n            if(!preparingClasses(classes))\n                return;\n        }\n        analyzingClasses(classes);\n    }\n\n    private boolean preparingClasses(Set<String> classes) {\n        MetadataSystem ms = createMetadataSystem();\n        Set<String> auxClasses = new TreeSet<>();\n        int count = 0;\n        for (String className : classes) {\n            if (!fireEvent(\"Reading classes\", className, count, classes.size()))\n                return false;\n            if(++count % options.classesPerFlush == 0) {\n                ms = createMetadataSystem();\n            }\n            TypeDefinition type;\n            try {\n                type = lookUp(ms, className);\n            } catch (Throwable t) {\n                addError(new ErrorMessage(null, className, null, null, -1, t));\n                continue;\n            }\n            if (type != null) {\n                for(ConstantPool.Entry entry : type.getConstantPool()) {\n                    if(entry instanceof TypeInfoEntry) {\n                        String depName = getMainType(((TypeInfoEntry)entry).getName());\n                        if(depName != null && !classes.contains(depName))\n                            auxClasses.add(depName);\n                    }\n                }\n                registry.populateDatabases(type);\n            }\n        }\n        if (!fireEvent(\"Reading classes\", null, classes.size(), classes.size()))\n            return false;\n        ms = createMetadataSystem();\n        count = 0;\n        for (String className : auxClasses) {\n            if (!fireEvent(\"Reading dep classes\", className, count, auxClasses.size()))\n                return false;\n            if(++count % options.classesPerFlush == 0) {\n                ms = createMetadataSystem();\n            }\n            TypeDefinition type;\n            try {\n                type = lookUp(ms, className);\n            } catch (Throwable t) {\n                addError(new ErrorMessage(null, className, null, null, -1, t));\n                continue;\n            }\n            if (type != null)\n                registry.populateDatabases(type);\n        }\n        return fireEvent(\"Reading dep classes\", null, auxClasses.size(), auxClasses.size());\n    }\n\n    MetadataSystem createMetadataSystem() {\n        return new MetadataSystem(loader) {\n            Set<String> loadedTypes = new HashSet<>();\n            \n            @Override\n            protected TypeDefinition resolveType(String descriptor, boolean mightBePrimitive) {\n                if(missingClasses.contains(descriptor)) {\n                    return null;\n                }\n                try {\n                    if(loadedTypes.add(descriptor))\n                        incStat(\"ClassLoadingEfficiency.Total\");\n                    if(classes.add(descriptor))\n                        incStat(\"ClassLoadingEfficiency\");\n                    return super.resolveType(descriptor, mightBePrimitive);\n                } catch (Throwable t) {\n                    addError(new ErrorMessage(null, descriptor, null, null, -1, t));\n                    missingClasses.add(descriptor);\n                    return null;\n                }\n            }\n        };\n    }\n\n    private TypeDefinition lookUp(MetadataSystem ms, String className) {\n        TypeReference tr = ms.lookupType(className);\n        if(tr == null) {\n            missingClasses.add(className);\n            return null;\n        }\n        return ms.resolve(tr);\n    }\n\n    private void analyzingClasses(Set<String> classes) {\n        MetadataSystem ms = createMetadataSystem();\n        classesCount.set(0);\n        for (String className : classes) {\n            if(classesCount.get() % options.classesPerFlush == 0)\n                ms = createMetadataSystem();\n            if (!fireEvent(\"Analyzing classes\", className, classesCount.get(), classes.size()))\n                return;\n            analyzeClass(ms, className);\n        }\n        if (!fireEvent(\"Analyzing classes\", null, classes.size(), classes.size()))\n            return;\n    }\n\n    void analyzeClass(MetadataSystem ms, String name) {\n        classesCount.incrementAndGet();\n        TypeDefinition type;\n        try {\n            type = lookUp(ms, name);\n        } catch (Throwable t) {\n            addError(new ErrorMessage(null, name, null, null, -1, t));\n            return;\n        }\n        if (type != null)\n            registry.analyzeClass(type);\n    }\n\n    public void addError(ErrorMessage msg) {\n        incStat(\"InternalErrors\");\n        errors.add(msg);\n    }\n\n    public void addWarning(Warning warning) {\n        if(warning.getScore() < getOptions().minScore)\n            return;\n        incStat(\"Warnings\");\n        warnings.add(warning);\n    }\n    \n    @Override\n    public Stream<Warning> warnings() {\n        return warnings.stream();\n    }\n\n    @Override\n    public Stream<ErrorMessage> errors() {\n        return errors.stream();\n    }\n\n    public void reportWarnings(PrintStream app) {\n        List<Warning> warns = new ArrayList<>(warnings);\n        warns.sort(Comparator.comparingInt(Warning::getScore).reversed().thenComparing(w -> w.getType().getName())\n            .thenComparing(Warning::getClassName));\n        warns.forEach(w -> app.append(w.toString()).append(\"\\n\"));\n    }\n\n    public void reportStats(PrintStream app) {\n        if (stat.isEmpty())\n            return;\n        app.append(\"Statistics:\\n\");\n        stat.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> {\n            String key = e.getKey();\n            Long value = e.getValue();\n            if(stat.containsKey(key+\".Total\"))\n                return;\n            int slashPos = key.indexOf('/');\n            if(slashPos >= 0) {\n                String partKey = key.substring(0, slashPos);\n                String totalKey = key.substring(slashPos+1);\n                Long totalValue = stat.getOrDefault(totalKey, 0L);\n                app.printf(Locale.ENGLISH, \"\\t%s: %d of %d (%.2f%%)%n\", partKey, value, totalValue, value*100.0/totalValue);\n            } else if(key.endsWith(\".Total\")) {\n                String partKey = key.substring(0, key.length()-\".Total\".length());\n                Long part = stat.getOrDefault(partKey, 0L);\n                app.printf(Locale.ENGLISH, \"\\t%s: %d of %d (%.2f%%)%n\", partKey, part, value, part*100.0/value);\n            } else \n                app.printf(Locale.ENGLISH, \"\\t%s: %d%n\", key, value);\n        });\n    }\n\n    public void reportErrors(PrintStream app) {\n        errors.forEach(msg -> app.append(msg.toString()).append(\"\\n\"));\n    }\n\n    public void reportWarningTypes(PrintStream out) {\n        registry.reportWarningTypes(out);\n    }\n\n    public void reportDatabases(PrintStream out) {\n        registry.reportDatabases(out);\n    }\n    \n    public void reportTitles(PrintStream out) {\n        registry.reportTitles(out);\n    }\n\n    public int getClassesCount() {\n        return classesCount.get();\n    }\n\n    public int getTotalClasses() {\n        return totalClasses;\n    }\n\n    public int getErrorCount() {\n        return errors.size();\n    }\n\n    public void incStat(String key) {\n        stat.merge(key, 1L, Long::sum);\n    }\n    \n    public Stream<WarningType> warningTypes() {\n        return registry.warningTypes();\n    }\n    \n    public WarningType getWarningType(String typeName) {\n        return registry.getWarningType(typeName);\n    }\n\n    public long getStat(String key) {\n        return stat.getOrDefault(key, 0L);\n    }\n\n    private static String getMainType(String internalName) {\n        if(internalName.startsWith(\"[\")) {\n            if(!internalName.endsWith(\";\"))\n                return null;\n            int pos = 0;\n            while(internalName.charAt(pos) == '[') pos++;\n            if(internalName.charAt(pos++) != 'L')\n                return null;\n            internalName = internalName.substring(pos, internalName.length()-1);\n        }\n        int lastSlash = internalName.lastIndexOf('/');\n        int dollar = internalName.indexOf('$');\n        if(dollar > lastSlash) {\n            return internalName.substring(0, dollar);\n        }\n        return internalName;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/analysis/ErrorMessage.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.analysis;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\nimport one.util.huntbugs.registry.Detector;\n\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class ErrorMessage {\n    private final String className;\n    private final String elementName;\n    private final String descriptor;\n    private final int line;\n    private final Throwable error;\n    private final String errorMessage;\n    private final String detector;\n    \n    public ErrorMessage(Detector detector, MemberReference method, int line, Throwable error) {\n        this(detector == null ? null : detector.toString(), method.getDeclaringType().getFullName(), method\n                .getFullName(), method.getSignature(), line, error);\n    }\n\n    public ErrorMessage(Detector detector, TypeDefinition type, Throwable error) {\n        this(detector == null ? null : detector.toString(), type.getFullName(), null, null, -1, error);\n    }\n    \n    public ErrorMessage(Detector detector, TypeDefinition type, String message) {\n        this(detector == null ? null : detector.toString(), type.getFullName(), null, null, -1, message);\n    }\n    \n    public ErrorMessage(Detector detector, MemberReference method, int line, String message) {\n        this(detector == null ? null : detector.toString(), method.getDeclaringType().getFullName(), method\n                .getFullName(), method.getSignature(), line, message);\n    }\n    \n    public ErrorMessage(String detector, String className, String elementName, String descriptor, int line, Throwable error) {\n        this.detector = detector;\n        this.className = className;\n        this.elementName = elementName;\n        this.descriptor = descriptor;\n        this.line = line;\n        this.error = error;\n        this.errorMessage = null;\n    }\n    \n    public ErrorMessage(String detector, String className, String elementName, String descriptor, int line, String message) {\n        this.detector = detector;\n        this.className = className;\n        this.elementName = elementName;\n        this.descriptor = descriptor;\n        this.line = line;\n        this.error = null;\n        this.errorMessage = message;\n    }\n    \n    public String getClassName() {\n        return className;\n    }\n\n    public String getElementName() {\n        return elementName;\n    }\n\n    public String getDescriptor() {\n        return descriptor;\n    }\n\n    public int getLine() {\n        return line;\n    }\n\n    public String getDetector() {\n        return detector;\n    }\n    \n    public String getError() {\n        if(errorMessage != null)\n            return errorMessage;\n        StringWriter sw = new StringWriter();\n        error.printStackTrace(new PrintWriter(sw));\n        return sw.toString();\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"Class: \").append(className);\n        if(line != -1)\n            sb.append(\"\\nLine: \").append(line);\n        if(elementName != null)\n            sb.append(\"\\nElement: \").append(elementName).append(\": \").append(descriptor);\n        if(detector != null)\n            sb.append(\"\\nDetector: \").append(detector);\n        sb.append(\"\\nError: \").append(getError());\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/analysis/HuntBugsResult.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.analysis;\n\nimport java.util.stream.Stream;\n\nimport one.util.huntbugs.warning.Messages;\nimport one.util.huntbugs.warning.Warning;\n\n/**\n * @author lan\n *\n */\npublic interface HuntBugsResult {\n    /**\n     * @return messages defined for this result\n     */\n    Messages getMessages();\n\n    /**\n     * @return warnings reported by this result\n     */\n    Stream<Warning> warnings();\n\n    /**\n     * @return internal errors reported by this result\n     */\n    Stream<ErrorMessage> errors();\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/assertions/AssertionData.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.assertions;\n\nimport java.util.Locale;\n\nimport one.util.huntbugs.warning.Warning;\n\n/**\n * @author Tagir Valeev\n *\n */\nclass AssertionData {\n    private final boolean hasWarning;\n    private final boolean isPrefix;\n    private final String type;\n    private final int minScore, maxScore;\n    \n    enum Status {\n        PASS, FAIL, NONE\n    }\n\n    AssertionData(boolean hasWarning, String type, int minScore, int maxScore) {\n        super();\n        this.hasWarning = hasWarning;\n        if(type.endsWith(\"*\")) {\n            this.isPrefix = true;\n            this.type = type.substring(0, type.length()-1);\n        } else {\n            this.isPrefix = false;\n            this.type = type;\n        }\n        this.minScore = minScore;\n        this.maxScore = maxScore;\n    }\n    \n    \n    Status check(Warning warning) {\n        boolean typeMatches;\n        if(isPrefix)\n            typeMatches = warning.getType().getName().toLowerCase(Locale.ENGLISH).startsWith(type.toLowerCase(Locale.ENGLISH));\n        else\n            typeMatches = warning.getType().getName().equalsIgnoreCase(type);\n        if(!typeMatches)\n            return Status.NONE;\n        if(!hasWarning)\n            return Status.FAIL;\n        int score = warning.getScore();\n        if(score < minScore || score > maxScore)\n            return Status.FAIL;\n        return Status.PASS;\n    }\n    \n    Status finalStatus() {\n        return hasWarning ? Status.FAIL : Status.PASS;\n    }\n\n    @Override\n    public String toString() {\n        if(hasWarning)\n            return \"AssertWarning(\"+type+(isPrefix?\"*\":\"\")+\"; score = \"+minScore+\"..\"+maxScore+\")\";\n\t\treturn \"AssertNoWarning(\"+type+(isPrefix?\"*\":\"\")+\")\";\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/assertions/MemberAsserter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.assertions;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.function.Consumer;\n\nimport one.util.huntbugs.assertions.AssertionData.Status;\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\nimport one.util.huntbugs.warning.Warning;\n\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.annotations.AnnotationParameter;\nimport com.strobel.assembler.metadata.annotations.ConstantAnnotationElement;\nimport com.strobel.assembler.metadata.annotations.CustomAnnotation;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class MemberAsserter {\n    public static final MemberAsserter EMPTY_ASSERTER = new MemberAsserter(null, null);\n    \n    private final List<AssertionData> data;\n    private final List<AssertionData> finalData;\n    private final MemberAsserter parent;\n    \n    private MemberAsserter(MemberAsserter parent, List<AssertionData> data) {\n        super();\n        this.parent = parent;\n        this.data = data;\n        this.finalData = data == null ? null : new ArrayList<>(data);\n    }\n\n    public static MemberAsserter forMember(MemberReference md) {\n        List<AssertionData> assertions = analyzeMember(md);\n        if(assertions.isEmpty())\n            return EMPTY_ASSERTER;\n        return new MemberAsserter(null, assertions);\n    }\n\n    public static MemberAsserter forMember(MemberAsserter parent, MemberReference md) {\n        List<AssertionData> assertions = analyzeMember(md);\n        if(assertions.isEmpty() && parent == EMPTY_ASSERTER)\n            return EMPTY_ASSERTER;\n        return new MemberAsserter(parent, assertions);\n    }\n    \n    private static List<AssertionData> analyzeMember(MemberReference md) {\n        List<AssertionData> assertions = new ArrayList<>();\n        if(md instanceof MethodDefinition && ((MethodDefinition) md).isSynthetic())\n            return assertions;\n        for(CustomAnnotation anno : md.getAnnotations()) {\n            if(anno.getAnnotationType().getFullName().equals(AssertWarning.class.getName())) {\n                String type = \"\";\n                int minScore = 0, maxScore = 100;\n                for(AnnotationParameter param : anno.getParameters()) {\n                    if(param.getMember().equals(\"value\"))\n                        type = (String) ((ConstantAnnotationElement)param.getValue()).getConstantValue();\n                    else if(param.getMember().equals(\"minScore\"))\n                        minScore = (int) ((ConstantAnnotationElement)param.getValue()).getConstantValue();\n                    else if(param.getMember().equals(\"maxScore\"))\n                        maxScore = (int) ((ConstantAnnotationElement)param.getValue()).getConstantValue();\n                }\n                assertions.add(new AssertionData(true, type, minScore, maxScore));\n            } else if(anno.getAnnotationType().getFullName().equals(AssertNoWarning.class.getName())) {\n                String type = \"\";\n                for(AnnotationParameter param : anno.getParameters()) {\n                    if(param.getMember().equals(\"value\"))\n                        type = (String) ((ConstantAnnotationElement)param.getValue()).getConstantValue();\n                }\n                assertions.add(new AssertionData(false, type, Warning.MIN_SCORE, Warning.MAX_SCORE));\n            }\n        }\n        return assertions;\n    }\n    \n    public void checkWarning(Consumer<String> errorConsumer, Warning warning) {\n        if(parent != null)\n            parent.checkWarning(errorConsumer, warning);\n        if(data == null)\n            return;\n        for(int i=0; i<data.size(); i++) {\n            AssertionData ad = data.get(i);\n            Status status = ad.check(warning);\n            if(status == Status.PASS)\n                finalData.set(i, null);\n            else if(status == Status.FAIL) {\n                errorConsumer.accept(\"Unexpected warning: \"+warning+\" (rule: \"+ad+\")\");\n            }\n        }\n    }\n    \n    public void checkFinally(Consumer<String> errorConsumer) {\n        if(finalData == null)\n            return;\n        for(ListIterator<AssertionData> it = finalData.listIterator(); it.hasNext(); ) {\n            AssertionData ad = it.next();\n            if(ad == null) continue;\n            Status status = ad.finalStatus();\n            if(status == Status.FAIL) {\n                errorConsumer.accept(\"Warning rule is not satisfied: \"+ad);\n            }\n        }        \n    }\n\n    public boolean isEmpty() {\n        return this == EMPTY_ASSERTER;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/db/DeclaredAnnotations.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.db;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.annotations.AnnotationElement;\nimport com.strobel.assembler.metadata.annotations.AnnotationParameter;\nimport com.strobel.assembler.metadata.annotations.CustomAnnotation;\nimport com.strobel.assembler.metadata.annotations.EnumAnnotationElement;\n\nimport one.util.huntbugs.registry.AbstractTypeDatabase;\nimport one.util.huntbugs.registry.anno.TypeDatabase;\nimport one.util.huntbugs.registry.anno.TypeDatabaseItem;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@TypeDatabase\npublic class DeclaredAnnotations extends AbstractTypeDatabase<DeclaredAnnotations.DeclaredAnnotation> {\n\n    public DeclaredAnnotations() {\n        super(type -> new DeclaredAnnotation());\n    }\n\n    @Override\n    protected void visitType(TypeDefinition td) {\n        if (!td.isAnnotation())\n            return;\n        DeclaredAnnotation da = getOrCreate(td);\n        for (CustomAnnotation ca : td.getAnnotations()) {\n            if (Types.is(ca.getAnnotationType(), Retention.class)) {\n                for (AnnotationParameter ap : ca.getParameters()) {\n                    if (ap.getMember().equals(\"value\")) {\n                        AnnotationElement value = ap.getValue();\n                        if (value instanceof EnumAnnotationElement) {\n                            EnumAnnotationElement enumValue = (EnumAnnotationElement) value;\n                            if (Types.is(enumValue.getEnumType(), RetentionPolicy.class)) {\n                                da.policy = RetentionPolicy.valueOf(enumValue.getEnumConstantName());\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    @TypeDatabaseItem(parentDatabase = DeclaredAnnotations.class)\n    public static class DeclaredAnnotation {\n        RetentionPolicy policy = RetentionPolicy.CLASS;\n\n        public RetentionPolicy getPolicy() {\n            return policy;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/db/FieldStats.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.db;\n\nimport java.util.ArrayDeque;\nimport java.util.Arrays;\nimport java.util.Deque;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.strobel.assembler.ir.Instruction;\nimport com.strobel.assembler.ir.OpCode;\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodBody;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.assembler.metadata.VariableReference;\n\nimport one.util.huntbugs.registry.AbstractTypeDatabase;\nimport one.util.huntbugs.registry.anno.TypeDatabase;\nimport one.util.huntbugs.registry.anno.TypeDatabaseItem;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@TypeDatabase\npublic class FieldStats extends AbstractTypeDatabase<FieldStats.TypeFieldStats>{\n    public static final int WRITE_CONSTRUCTOR = 0x0001;\n    public static final int WRITE_CLASS = 0x0002;\n    public static final int WRITE_PACKAGE = 0x0004;\n    public static final int WRITE_OUTSIDE = 0x0008;\n    public static final int WRITE_NONNULL = 0x0010;\n    public static final int WRITE = WRITE_CONSTRUCTOR | WRITE_CLASS | WRITE_PACKAGE | WRITE_OUTSIDE;\n    public static final int READ_CLASS = 0x0100;\n    public static final int READ_PACKAGE = 0x0200;\n    public static final int READ_OUTSIDE = 0x0400;\n    public static final int READ = READ_CLASS | READ_PACKAGE | READ_OUTSIDE;\n    public static final int ACCESS = READ | WRITE;\n    public static final int UNRESOLVED = 0x10000000;\n    \n    public FieldStats() {\n        super(name -> new TypeFieldStats());\n    }\n    \n    static final class SimpleStack {\n        private static final Object UNKNOWN_CONST = new Object();\n        private static final Object NULL_CONST = new Object();\n\n        Set<Instruction> seenLabels = new HashSet<>();\n        Deque<Object> constStack = new ArrayDeque<>();\n        Object[] locals;\n        \n        SimpleStack(int maxLocals) {\n            locals = new Object[maxLocals];\n            Arrays.fill(locals, UNKNOWN_CONST);\n        }\n        \n        void registerJump(Instruction instr) {\n            if(instr.getOperandCount() == 1 && instr.getOperand(0) instanceof Instruction) {\n                seenLabels.add(instr.getOperand(0));\n            }\n        }\n\n        void preprocess(Instruction instr) {\n            if(seenLabels.contains(instr)) {\n                constStack.clear();\n                Arrays.fill(locals, UNKNOWN_CONST);\n            }\n        }\n        \n        void set(int slot, Object cst) {\n            locals[slot] = cst;\n        }\n        \n        void setUnknown(int slot) {\n            locals[slot] = UNKNOWN_CONST;\n        }\n        \n        Object get(int slot) {\n            return locals[slot];\n        }\n        \n        void pushUnknown() {\n            push(UNKNOWN_CONST);\n        }\n        \n        void push(Object cst) {\n            constStack.add(cst == null ? NULL_CONST : cst);\n        }\n        \n        Object poll() {\n            Object cst = constStack.pollLast();\n            return cst == null ? UNKNOWN_CONST : cst == NULL_CONST ? null : cst; \n        }\n\n        void clear() {\n            constStack.clear();\n        }\n    }\n    \n    @Override\n    protected void visitType(TypeDefinition td) {\n        getOrCreate(td);\n        for(MethodDefinition md : td.getDeclaredMethods()) {\n            MethodBody body = md.getBody();\n            if(body != null) {\n                SimpleStack ss = new SimpleStack(body.getMaxLocals());\n                for(Instruction instr : body.getInstructions()) {\n                    ss.registerJump(instr);\n                }\n                for(Instruction instr : body.getInstructions()) {\n                    ss.preprocess(instr);\n                    switch(instr.getOpCode()) {\n                    case ALOAD_0:\n                    case ALOAD_1:\n                    case ALOAD_2:\n                    case ALOAD_3:\n                        ss.push(ss.get(instr.getOpCode().getCode()-OpCode.ALOAD_0.getCode()));\n                        continue;\n                    case ALOAD:\n                        ss.push(ss.get(((VariableReference)instr.getOperand(0)).getSlot()));\n                        continue;\n                    case ASTORE_0:\n                    case ASTORE_1:\n                    case ASTORE_2:\n                    case ASTORE_3:\n                        ss.set(instr.getOpCode().getCode()-OpCode.ASTORE_0.getCode(), ss.poll());\n                        continue;\n                    case ASTORE:\n                        ss.set(((VariableReference)instr.getOperand(0)).getSlot(), ss.poll());\n                        continue;\n                    case LDC:\n                    case LDC_W:\n                        ss.push(instr.getOperand(0));\n                        continue;\n                    case INVOKESTATIC:\n                    case INVOKEVIRTUAL: {\n                        MethodReference mr = instr.getOperand(0);\n                        if (mr.getName().equals(\"newUpdater\") && mr.getDeclaringType().getPackageName().equals(\n                            \"java.util.concurrent.atomic\") || mr.getName().equals(\"getDeclaredField\") && Types.is(mr.getDeclaringType(),\n                                Class.class)) {\n                            Object fieldName = ss.poll();\n                            if(mr.getParameters().size() == 3) {\n                                ss.poll(); // field type for AtomicReferenceFieldUpdater\n                            }\n                            Object type = ss.poll();\n                            linkUncontrolled(type, fieldName);\n                        }\n                        if (mr.getDeclaringType().getInternalName().equals(\"java/lang/invoke/MethodHandles$Lookup\")\n                            && (mr.getName().equals(\"findGetter\") || mr.getName().equals(\"findSetter\") || mr\n                                    .getName().startsWith(\"findStatic\"))) {\n                            ss.poll();\n                            Object fieldName = ss.poll();\n                            Object type = ss.poll();\n                            linkUncontrolled(type, fieldName);\n                        }\n                        if (mr.getName().equals(\"getDeclaredFields\") && Types.is(mr.getDeclaringType(), Class.class)) {\n                            Object type = ss.poll();\n                            linkUncontrolled(type, null);\n                        }\n                        break;\n                    }\n                    case ACONST_NULL:\n                        ss.push(null);\n                        continue;\n                    case PUTFIELD:\n                    case PUTSTATIC: {\n                        FieldReference fr = (FieldReference)instr.getOperand(0);\n                        FieldDefinition fd = fr.resolve();\n                        if(fd != null) {\n                            if(fd.isSynthetic())\n                                break;\n                            fr = fd;\n                        } // fd == null case is necessary to workaround procyon problem#301\n                        Object value = ss.poll();\n                        if(instr.getOpCode() == OpCode.PUTFIELD)\n                            ss.poll();\n                        getOrCreate(fr.getDeclaringType()).link(md, fr,\n                            instr.getOpCode() == OpCode.PUTSTATIC, true, value == null);\n                        continue;\n                    }\n                    case GETFIELD:\n                    case GETSTATIC:\n                        FieldReference fr = (FieldReference)instr.getOperand(0);\n                        FieldDefinition fd = fr.resolve();\n                        if(fd != null) {\n                            if(fd.isSynthetic())\n                                break;\n                            fr = fd;\n                        } // fd == null case is necessary to workaround procyon problem#301\n                        if(instr.getOpCode() == OpCode.GETFIELD)\n                            ss.poll();\n                        getOrCreate(fr.getDeclaringType()).link(md, fr,\n                            instr.getOpCode() == OpCode.GETSTATIC, false, false);\n                        ss.pushUnknown();\n                        continue;\n                    default:\n                    }\n                    ss.clear();\n                }\n            }\n        }\n    }\n    \n    private void linkUncontrolled(Object type, Object fieldName) {\n        if(type instanceof TypeReference) {\n            TypeFieldStats tfs = getOrCreate((TypeReference) type);\n            if(fieldName instanceof String)\n                tfs.linkUncontrolled((String) fieldName);\n            else\n                tfs.linkUncontrolled();\n        }\n    }\n\n    public int getFlags(FieldReference fr) {\n        TypeFieldStats fs = get(fr.getDeclaringType());\n        return fs == null ? UNRESOLVED : fs.getFlags(fr.getName()); \n    }\n\n    @TypeDatabaseItem(parentDatabase=FieldStats.class)\n    public static class TypeFieldStats {\n        // Can be null if the whole type is uncontrolled\n        private Map<String, Integer> fieldRecords = new HashMap<>();\n        \n        void linkUncontrolled(String fieldName) {\n            if(fieldRecords != null)\n                fieldRecords.put(fieldName, ACCESS | WRITE_NONNULL);\n        }\n        \n        public int getFlags(String name) {\n            if(fieldRecords == null) {\n                return ACCESS | WRITE_NONNULL;\n            }\n            return fieldRecords.getOrDefault(name, 0);\n        }\n\n        void linkUncontrolled() {\n            fieldRecords = null;\n        }\n\n        void link(MethodDefinition src, FieldReference fr, boolean isStatic, boolean write, boolean hadNull) {\n            if(fieldRecords == null)\n                return;\n            int prevStatus = fieldRecords.getOrDefault(fr.getName(), 0);\n            int curStatus = prevStatus;\n            if(src.getDeclaringType().isEquivalentTo(fr.getDeclaringType())) {\n                if(write && (src.isConstructor() && !isStatic || src.isTypeInitializer() && isStatic)) {\n                    curStatus |= WRITE_CONSTRUCTOR;\n                } else {\n                    curStatus |= write ? WRITE_CLASS : READ_CLASS;\n                }\n            } else if(src.getDeclaringType().getPackageName().equals(fr.getDeclaringType().getPackageName())) {\n                curStatus |= write ? WRITE_PACKAGE : READ_PACKAGE;\n            } else {\n                curStatus |= write ? WRITE_OUTSIDE : READ_OUTSIDE;\n            }\n            if(write && !hadNull) {\n                curStatus |= WRITE_NONNULL;\n            }\n            if(prevStatus != curStatus) {\n                fieldRecords.put(fr.getName(), curStatus);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/db/Hierarchy.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.db;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.IMetadataResolver;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.registry.AbstractTypeDatabase;\nimport one.util.huntbugs.registry.anno.TypeDatabase;\nimport one.util.huntbugs.registry.anno.TypeDatabaseItem;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\n@TypeDatabase\npublic class Hierarchy extends AbstractTypeDatabase<Hierarchy.TypeHierarchy> {\n    public Hierarchy() {\n        super(TypeHierarchy::new);\n    }\n\n    @Override\n    protected void visitType(TypeDefinition td) {\n        TypeHierarchy th = getOrCreate(td);\n        th.flags = td.getFlags();\n        link(th, td.getBaseType());\n        for (TypeReference id : td.getExplicitInterfaces())\n            link(th, id);\n    }\n    \n    public boolean isOverridden(MethodDefinition md) {\n        if(md.isStatic() || md.isFinal() || md.getDeclaringType().isFinal())\n            return false;\n        IMetadataResolver resolver = md.getDeclaringType().getResolver();\n        MemberInfo mi = new MemberInfo(md);\n        TypeHierarchy th = get(md.getDeclaringType());\n        return th != null && th.isOverridden(resolver, mi);\n    }\n    \n    private void link(TypeHierarchy th, TypeReference superType) {\n        if (superType == null || Types.isObject(superType))\n            return;\n        TypeHierarchy superTh = getOrCreate(superType);\n        th.superClasses.add(superTh);\n        superTh.subClasses.add(th);\n    }\n\n    @TypeDatabaseItem(parentDatabase = Hierarchy.class)\n    public static class TypeHierarchy {\n        final String internalName;\n        long flags = Flags.LOAD_BODY_FAILED;\n        final Set<TypeHierarchy> superClasses = new HashSet<>();\n        final Set<TypeHierarchy> subClasses = new HashSet<>();\n\n        public TypeHierarchy(String name) {\n            this.internalName = name;\n        }\n\n        public String getInternalName() {\n            return internalName;\n        }\n\n        public Set<TypeHierarchy> getSuperClasses() {\n            return Collections.unmodifiableSet(superClasses);\n        }\n\n        public Set<TypeHierarchy> getSubClasses() {\n            return Collections.unmodifiableSet(subClasses);\n        }\n        \n        boolean isOverridden(IMetadataResolver resolver, MemberInfo mi) {\n            for(TypeHierarchy th : subClasses) {\n                TypeReference str = resolver.lookupType(th.internalName);\n                if(str == null)\n                    continue;\n                TypeDefinition std = resolver.resolve(str);\n                if(std == null)\n                    continue;\n                MethodDefinition res = Methods.findMethod(std, mi);\n                if(res != null)\n                    return true;\n            }\n            for(TypeHierarchy th : subClasses) {\n                if(th.isOverridden(resolver, mi))\n                    return true;\n            }\n            return false;\n        }\n        \n        public boolean isResolved() {\n            return !hasFlag(Flags.LOAD_BODY_FAILED);\n        }\n        \n        public boolean hasFlag(long flag) {\n            return Flags.testAny(flags, flag);\n        }\n        \n        @Override\n        public String toString() {\n            return internalName;\n        }\n\n        public boolean hasSubClasses() {\n            return !subClasses.isEmpty();\n        }\n        \n        public boolean hasSubClassesOutOfPackage() {\n            return !subClasses.stream().allMatch(sc -> Types.samePackage(internalName, sc.getInternalName()));\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/db/MethodStats.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.db;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport com.strobel.assembler.ir.Instruction;\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.MethodBody;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.registry.AbstractTypeDatabase;\nimport one.util.huntbugs.registry.anno.TypeDatabase;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author lan\n *\n */\n@TypeDatabase\npublic class MethodStats extends AbstractTypeDatabase<Boolean> {\n    public static final long METHOD_MAY_HAVE_SIDE_EFFECT = 0x1;\n    public static final long METHOD_MAY_THROW = 0x2;\n    public static final long METHOD_MAY_RETURN_NORMALLY = 0x4;\n    public static final long METHOD_HAS_BODY = 0x8;\n    public static final long METHOD_NON_TRIVIAL = 0x10;\n    public static final long METHOD_FINAL = 0x20;\n    public static final long METHOD_SUPPORTED = 0x40;\n\n    Map<MemberInfo, MethodData> data = new HashMap<>();\n\n    public MethodStats() {\n        super(type -> Boolean.TRUE);\n    }\n\n    private MethodData getMethodData(MethodDefinition md) {\n        MemberInfo mi = new MemberInfo(md);\n        MethodData mdata = data.get(mi);\n        if (mdata != null)\n            return mdata;\n        if (md.isAbstract()) {\n            MethodDefinition superMd = Methods.findSuperMethod(md);\n            if (superMd != null)\n                mdata = getMethodData(superMd);\n        }\n        if (mdata == null) {\n            mdata = new MethodData();\n        }\n        data.put(mi, mdata);\n        return mdata;\n    }\n\n    @Override\n    protected void visitType(TypeDefinition td) {\n        for (MethodDefinition md : td.getDeclaredMethods()) {\n            MethodData mdata = getMethodData(md);\n            if (md.isFinal() || td.isFinal() || md.isStatic() || md.isPrivate()) {\n                mdata.flags |= METHOD_FINAL;\n            }\n            visitMethod(mdata, md);\n            for (MethodDefinition superMethod : Methods.findSuperMethods(md)) {\n                getMethodData(superMethod).addSubMethod(mdata);\n            }\n        }\n    }\n\n    public MethodData getStats(MemberInfo mi) {\n        return data.get(mi);\n    }\n\n    public MethodData getStats(MethodReference mr) {\n        return data.get(new MemberInfo(mr));\n    }\n\n    private void visitMethod(MethodData mdata, MethodDefinition md) {\n        MethodBody body = md.getBody();\n        if (Flags.testAny(md.getFlags(), Flags.NATIVE)) {\n            mdata.flags |= METHOD_MAY_HAVE_SIDE_EFFECT | METHOD_MAY_RETURN_NORMALLY | METHOD_MAY_THROW\n                | METHOD_NON_TRIVIAL | METHOD_SUPPORTED;\n        }\n        if (body != null) {\n            visitBody(mdata, body);\n        }\n    }\n\n    private void visitBody(MethodData mdata, MethodBody body) {\n        mdata.flags |= METHOD_HAS_BODY;\n        if (body.getInstructions().size() > 2) {\n            mdata.flags |= METHOD_NON_TRIVIAL;\n        }\n        boolean sawUnsupported = false, sawOtherNew = false;\n        for (Instruction instr : body.getInstructions()) {\n            switch (instr.getOpCode()) {\n            case NEW: {\n                TypeReference tr = (TypeReference) instr.getOperand(0);\n                if (tr.getInternalName().equals(\"java/lang/UnsupportedOperationException\")) {\n                    sawUnsupported = true;\n                } else {\n                    sawOtherNew = true;\n                }\n                break;\n            }\n            case INVOKEINTERFACE:\n            case INVOKESPECIAL:\n            case INVOKESTATIC:\n            case INVOKEVIRTUAL: {\n                MethodReference mr = (MethodReference) instr.getOperand(0);\n                if (!Methods.isSideEffectFree(mr)) {\n                    mdata.flags |= METHOD_MAY_HAVE_SIDE_EFFECT;\n                }\n                if (Methods.knownToThrow(mr)) {\n                    mdata.flags |= METHOD_MAY_THROW;\n                }\n                break;\n            }\n            case GETFIELD:\n            case GETSTATIC:\n                if ((mdata.flags & METHOD_MAY_HAVE_SIDE_EFFECT) == 0) {\n                    FieldReference fr = (FieldReference) instr.getOperand(0);\n                    FieldDefinition fd = fr.resolve();\n                    if (fd != null && Flags.testAny(fd.getFlags(), Flags.VOLATILE)) {\n                        mdata.flags |= METHOD_MAY_HAVE_SIDE_EFFECT;\n                    }\n                }\n                break;\n            case PUTFIELD:\n            case PUTSTATIC:\n            case INVOKEDYNAMIC:\n            case AASTORE:\n            case DASTORE:\n            case BASTORE:\n            case CASTORE:\n            case SASTORE:\n            case IASTORE:\n            case LASTORE:\n            case FASTORE:\n                mdata.flags |= METHOD_MAY_HAVE_SIDE_EFFECT;\n                break;\n            case ATHROW:\n                mdata.flags |= METHOD_MAY_THROW;\n                break;\n            case ARETURN:\n            case IRETURN:\n            case LRETURN:\n            case FRETURN:\n            case DRETURN:\n            case RETURN:\n                mdata.flags |= METHOD_MAY_RETURN_NORMALLY;\n                break;\n            default:\n            }\n        }\n        if (!mdata.testAny(METHOD_MAY_THROW, true) || mdata.testAny(METHOD_MAY_RETURN_NORMALLY, true) || !sawUnsupported\n            || sawOtherNew) {\n            mdata.flags |= METHOD_SUPPORTED;\n        }\n    }\n\n    public static class MethodData {\n        private List<MethodData> subMethods;\n        long flags;\n\n        void addSubMethod(MethodData md) {\n            if (md == this)\n                return;\n            if (subMethods == null) {\n                subMethods = new ArrayList<>();\n            }\n            subMethods.add(md);\n        }\n\n        public boolean testAny(long flag, boolean exact) {\n            if ((flags & flag) != 0)\n                return true;\n            if (!exact && subMethods != null) {\n                for (MethodData subMethod : subMethods) {\n                    if (subMethod.testAny(flag, false))\n                        return true;\n                }\n            }\n            return false;\n        }\n\n        public boolean mayHaveSideEffect(boolean exact) {\n            if (exact || ((flags & METHOD_FINAL) != 0)) {\n                return testAny(METHOD_MAY_HAVE_SIDE_EFFECT, true);\n            }\n            if (testAny(METHOD_MAY_HAVE_SIDE_EFFECT, false) || !testAny(METHOD_NON_TRIVIAL, false))\n                return true;\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/db/Mutability.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.db;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.registry.AbstractTypeDatabase;\nimport one.util.huntbugs.registry.anno.TypeDatabase;\n\n/**\n * @author shustkost\n *\n */\n@TypeDatabase\npublic class Mutability extends AbstractTypeDatabase<Boolean> {\n    public Mutability() {\n        super(type -> Boolean.TRUE);\n    }\n\n    @Override\n    protected void visitType(TypeDefinition td) {\n        if(!td.isPublic())\n            return;\n        for(FieldDefinition fd : td.getDeclaredFields()) {\n            if(!fd.isStatic() && !fd.isFinal() && fd.isPublic()) {\n                getOrCreate(td);\n                return;\n            }\n        }\n    }\n    \n    public boolean isKnownMutable(TypeReference tr) {\n        return get(tr.getInternalName()) != null;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/AbandonedStream.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.stream.BaseStream;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"AbandonedStream\", maxScore=80)\n@WarningDefinition(category=\"BadPractice\", name=\"StreamMethodMayNotReturnItself\", maxScore=30)\npublic class AbandonedStream {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS, minVersion=8)\n    public void visit(Expression expr, MethodContext mc) {\n        if(expr.getCode() == AstCode.InvokeInterface) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if(mr.getReturnType().getPackageName().equals(\"java.util.stream\")\n                    && Types.isBaseStream(mr.getReturnType())) {\n                // intermediate stream operation\n                if(mc.isAnnotated() && !Inf.BACKLINK.findTransitiveUsages(expr, true).findAny().isPresent()) {\n                    // .parallel()/.sequential()/.onClose()/.unordered() excluded as may return itself\n                    if(Types.is(mr.getReturnType(), BaseStream.class)) {\n                        mc.report(\"StreamMethodMayNotReturnItself\", 0, expr);\n                    } else {\n                        mc.report(\"AbandonedStream\", 0, expr);\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/AppendObjectOutputStream.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.LocationRole;\nimport one.util.huntbugs.warning.Role.TypeRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"AppendObjectOutputStream\", maxScore = 65)\npublic class AppendObjectOutputStream {\n    private static final LocationRole STREAM_CREATED_AT = LocationRole.forName(\"STREAM_CREATED_AT\");\n    private static final TypeRole OOS_TYPE = TypeRole.forName(\"OOS_TYPE\");\n    \n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if (expr.getCode() != AstCode.InitObject)\n            return;\n        MethodReference ctor = (MethodReference) expr.getOperand();\n        if (!ctor.getDeclaringType().getInternalName().equals(\"java/io/ObjectOutputStream\")\n            || !ctor.getSignature().equals(\"(Ljava/io/OutputStream;)V\"))\n            return;\n        Expression outStream = Exprs.getChild(expr, 0);\n        while (isBufferedStream(outStream))\n            outStream = Exprs.getChild(outStream, 0);\n        Expression target = ValuesFlow.findFirst(outStream, AppendObjectOutputStream::isAppendOutput);\n        if (target != null) {\n            mc.report(\"AppendObjectOutputStream\", 0, expr, STREAM_CREATED_AT.create(mc, target), OOS_TYPE.create(ctor\n                    .getDeclaringType()));\n        }\n    }\n\n    private static boolean isBufferedStream(Expression expr) {\n        if (expr.getCode() == AstCode.InitObject) {\n            MethodReference ctor = (MethodReference) expr.getOperand();\n            if (ctor.getDeclaringType().getInternalName().equals(\"java/io/BufferedOutputStream\")\n                && ctor.getSignature().equals(\"(Ljava/io/OutputStream;)V\")) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private static boolean isAppendOutput(Expression expr) {\n        if (expr.getCode() == AstCode.InitObject) {\n            MethodReference ctor = (MethodReference) expr.getOperand();\n            if (ctor.getDeclaringType().getInternalName().equals(\"java/io/FileOutputStream\")\n                && (ctor.getSignature().equals(\"(Ljava/io/File;Z)V\") || ctor.getSignature().equals(\n                    \"(Ljava/lang/String;Z)V\"))) {\n                Object constant = Nodes.getConstant(expr.getArguments().get(1));\n                if (constant instanceof Integer && ((Integer) constant).intValue() == 1) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/ArrayRangeCheck.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Role.NumberRole;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"ArrayIndexNegative\", maxScore = 85)\n@WarningDefinition(category = \"Correctness\", name = \"ArrayIndexOutOfRange\", maxScore = 85)\n@WarningDefinition(category = \"Correctness\", name = \"ArrayOffsetOutOfRange\", maxScore = 85)\n@WarningDefinition(category = \"Correctness\", name = \"ArrayLengthOutOfRange\", maxScore = 85)\npublic class ArrayRangeCheck {\n    private static final NumberRole MAX_LENGTH = NumberRole.forName(\"MAX_LENGTH\");  \n    private static final long IMPOSSIBLE_ARRAY_LENGTH = Integer.MAX_VALUE + 1L;\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(MethodContext mc, NodeChain nc, Expression expr) {\n        if ((expr.getCode() == AstCode.LoadElement || expr.getCode() == AstCode.StoreElement)\n                && !nc.isInTry(\"java/lang/ArrayIndexOutOfBoundsException\", \"java/lang/IndexOutOfBoundsException\")) {\n            Integer idx = checkNegative(mc, expr, Nodes.getConstant(expr.getArguments().get(1)));\n            if (idx != null) {\n                long maxLength = getMaxLength(expr.getArguments().get(0));\n                if (idx >= maxLength) {\n                    mc.report(\"ArrayIndexOutOfRange\", 0, expr.getArguments().get(0), Roles.NUMBER.create(idx),\n                        MAX_LENGTH.create(maxLength));\n                }\n            }\n        } else if (expr.getCode() == AstCode.InvokeStatic) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if (Types.is(mr.getDeclaringType(), System.class) && mr.getName().equals(\"arraycopy\") && expr.getArguments()\n                    .size() == 5 && !nc.isInTry(\"java/lang/IndexOutOfBoundsException\")) {\n                Integer srcPos = checkNegative(mc, expr, Nodes.getConstant(expr.getArguments().get(1)));\n                Integer destPos = checkNegative(mc, expr, Nodes.getConstant(expr.getArguments().get(3)));\n                Integer length = checkNegative(mc, expr, Nodes.getConstant(expr.getArguments().get(4)));\n                long srcLen = getMaxLength(expr.getArguments().get(0));\n                long destLen = getMaxLength(expr.getArguments().get(2));\n                if(srcPos != null) {\n                    if(srcPos > srcLen) {\n                        mc.report(\"ArrayOffsetOutOfRange\", 0, expr.getArguments().get(0), Roles.NUMBER.create(srcPos),\n                            MAX_LENGTH.create(srcLen));\n                    } else {\n                        srcLen -= srcPos;\n                    }\n                }\n                if(destPos != null) {\n                    if(destPos > destLen) {\n                        mc.report(\"ArrayOffsetOutOfRange\", 0, expr.getArguments().get(2), Roles.NUMBER.create(destPos),\n                            MAX_LENGTH.create(destLen));\n                    } else {\n                        destLen -= destPos;\n                    }\n                }\n                long maxLen = Math.min(srcLen, destLen);\n                if(length != null && length > maxLen) {\n                    mc.report(\"ArrayLengthOutOfRange\", 0, expr.getArguments().get(2), Roles.NUMBER.create(length),\n                        MAX_LENGTH.create(maxLen));\n                }\n            }\n        }\n    }\n\n    private static Integer checkNegative(MethodContext mc, Expression expr, Object idxObj) {\n        if (idxObj instanceof Integer) {\n            int idx = (int) idxObj;\n            if (idx >= 0)\n                return idx;\n            mc.report(\"ArrayIndexNegative\", 0, expr.getArguments().get(0), Roles.NUMBER.create(idx));\n        }\n        return null;\n    }\n\n    // Returns max possible length of the expression producing an array\n    private static long getMaxLength(Expression expression) {\n        return ValuesFlow.<Long>reduce(expression, expr -> {\n            switch (expr.getCode()) {\n            case InitArray:\n                return (long) expr.getArguments().size();\n            case NewArray: {\n                Object length = Nodes.getConstant(expr.getArguments().get(0));\n                if (length instanceof Integer)\n                    return (long) (Integer) length;\n                break;\n            }\n            case InvokeVirtual: {\n                MethodReference mr = (MethodReference) expr.getOperand();\n                if (mr.getName().equals(\"clone\") && mr.getErasedSignature().startsWith(\"()\")) {\n                    return getMaxLength(Exprs.getChild(expr, 0));\n                }\n                break;\n            }\n            case CheckCast:\n                return getMaxLength(Exprs.getChild(expr, 0));\n            default:\n                break;\n            }\n            return IMPOSSIBLE_ARRAY_LENGTH;\n        }, Math::max, len -> len == IMPOSSIBLE_ARRAY_LENGTH);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/AtomicConcurrent.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Role.MemberRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Multithreading\", name = \"NonAtomicOperationOnConcurrentMap\", maxScore = 70)\npublic class AtomicConcurrent {\n    private static final MemberRole FIRST_METHOD = MemberRole.forName(\"FIRST_METHOD\"); \n    private static final MemberRole SECOND_METHOD = MemberRole.forName(\"SECOND_METHOD\"); \n    \n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc, MethodDefinition md) {\n        if (expr.getCode() == AstCode.InvokeVirtual || expr.getCode() == AstCode.InvokeInterface) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if (mr.getName().equals(\"put\")) {\n                TypeReference tr = ValuesFlow.reduceType(Exprs.getChild(expr, 0));\n                String typeName = tr == null ? mr.getDeclaringType().getInternalName() : tr.getInternalName();\n                if (typeName.equals(\"java/util/concurrent/ConcurrentHashMap\") || typeName\n                            .equals(\"java/util/concurrent/ConcurrentSkipListMap\")) {\n                    Expression self = expr.getArguments().get(0);\n                    Expression key = expr.getArguments().get(1);\n                    Expression value = expr.getArguments().get(2);\n                    Expression prevCall = null;\n                    int priority = 0;\n                    while (prevCall == null && nc != null) {\n                        if (nc.getNode() instanceof Condition) {\n                            Expression cond = ((Condition) nc.getNode()).getCondition();\n                            prevCall = Exprs.findExpression(cond, child -> isGetOrContains(self, key, child));\n                            if (prevCall == null) {\n                                prevCall = Exprs\n                                        .findExpressionWithSources(cond, child -> isGetOrContains(self, key, child));\n                                priority = 10;\n                            }\n                        }\n                        nc = nc.getParent();\n                    }\n                    if (prevCall == null) {\n                        priority = 0;\n                        prevCall = Exprs.findExpression(expr.getArguments().get(2), child -> isGetOrContains(self, key,\n                            child));\n                        if (prevCall == null) {\n                            prevCall = Exprs.findExpressionWithSources(expr.getArguments().get(2),\n                                child -> isGetOrContains(self, key, child));\n                            priority = 10;\n                        }\n                    }\n                    if(nc != null && nc.isSynchronized() || Flags.testAny(md.getFlags(), Flags.SYNCHRONIZED)) {\n                        priority += 40;\n                    }\n                    if(Types.isImmutable(value.getInferredType())) {\n                        priority += 30;\n                    }\n                    if (prevCall != null) {\n                        mc.report(\"NonAtomicOperationOnConcurrentMap\", priority, self, FIRST_METHOD.create(\n                            (MemberReference) prevCall.getOperand()), SECOND_METHOD.create(mr));\n                    }\n                }\n            }\n        }\n    }\n\n    private boolean isGetOrContains(Expression self, Expression key, Expression call) {\n        if (call.getCode() != AstCode.InvokeVirtual && call.getCode() != AstCode.InvokeInterface)\n            return false;\n        MethodReference mr = (MethodReference) call.getOperand();\n        if (!mr.getName().equals(\"containsKey\") && !mr.getName().equals(\"get\"))\n            return false;\n        if (!Nodes.isEquivalent(self, call.getArguments().get(0)))\n            return false;\n        if (!Nodes.isEquivalent(key, call.getArguments().get(1)))\n            return false;\n        return true;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/AverageComputation.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"BadPractice\", name = \"AverageComputationCouldOverflow\", maxScore = 35)\npublic class AverageComputation {\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visitExpression(Expression expr, MethodContext mc) {\n        if (expr.getCode() != AstCode.LoadElement && expr.getCode() != AstCode.StoreElement)\n            return;\n        Expression idx = Exprs.getChild(expr, 1);\n        if (!isDiv2(idx))\n            return;\n        Expression orig = Exprs.getChild(idx, 0);\n        if (orig.getCode() != AstCode.Add)\n            return;\n        mc.report(\"AverageComputationCouldOverflow\", 0, idx);\n    }\n\n    private boolean isDiv2(Expression idx) {\n        return (idx.getCode() == AstCode.Div && Integer.valueOf(2).equals(Nodes.getConstant(idx.getArguments().get(1))))\n            || (idx.getCode() == AstCode.Shr && Integer.valueOf(1).equals(Nodes.getConstant(idx.getArguments().get(1))));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/BadMath.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.NumberRole;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"RemOne\", maxScore = 80)\n@WarningDefinition(category = \"Correctness\", name = \"CompareBitAndIncompatible\", maxScore = 75)\n@WarningDefinition(category = \"Correctness\", name = \"CompareBitOrIncompatible\", maxScore = 75)\n@WarningDefinition(category = \"RedundantCode\", name = \"UselessOrWithZero\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"UselessAndWithMinusOne\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"UselessAndWithZero\", maxScore = 70)\n@WarningDefinition(category = \"Correctness\", name = \"BitCheckGreaterNegative\", maxScore = 80)\n@WarningDefinition(category = \"Correctness\", name = \"BitShiftInvalidAmount\", maxScore = 75)\n@WarningDefinition(category = \"Correctness\", name = \"BitShiftWrongPriority\", maxScore = 70)\n@WarningDefinition(category = \"BadPractice\", name = \"BitCheckGreater\", maxScore = 35)\n@WarningDefinition(category = \"Correctness\", name = \"BitOrSignedByte\", maxScore = 50)\n@WarningDefinition(category = \"Correctness\", name = \"BitAddSignedByte\", maxScore = 35)\npublic class BadMath {\n    private static final NumberRole COMPARED_TO = NumberRole.forName(\"COMPARED_TO\");\n    private static final NumberRole AND_OPERAND = NumberRole.forName(\"AND_OPERAND\");\n    private static final NumberRole OR_OPERAND = NumberRole.forName(\"OR_OPERAND\");\n    \n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void checkWrongPriority(Expression expr, MethodContext mc, MethodDefinition md) {\n        if(expr.getCode() == AstCode.Shl) {\n            Expression leftOp = expr.getArguments().get(0);\n            Expression rightOp = expr.getArguments().get(1);\n            if(rightOp.getCode() == AstCode.Add) {\n                Expression leftAddend = rightOp.getArguments().get(0);\n                Object leftConst = Nodes.getConstant(leftAddend);\n                Expression rightAddend = rightOp.getArguments().get(1);\n                if(leftConst instanceof Integer && !Integer.valueOf(1).equals(Nodes.getConstant(leftOp))) {\n                    int priority = 0;\n                    int c = (Integer)leftConst;\n                    if(c < 32 || (c < 64 && leftOp.getExpectedType().getSimpleType() == JvmType.Long)) {\n                        if(!Methods.isHashCodeMethod(md) || !Inf.BACKLINK.findTransitiveUsages(expr, false).allMatch(e -> e.getCode() == AstCode.Return)) {\n                            priority += 10;\n                            if(c == 16) {\n                                priority += 5;\n                            } else if(c != 8) {\n                                priority += 10;\n                            }\n                            if(rightAddend.getCode() != AstCode.And) {\n                                priority += 10;\n                            }\n                        }\n                        mc.report(\"BitShiftWrongPriority\", priority, expr, Roles.NUMBER.create(c));\n                    }\n                }\n            }\n        }\n    }\n    \n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc) {\n        TypeReference inferredType = expr.getInferredType();\n        if (inferredType == null)\n            return;\n        JvmType exprType = inferredType.getSimpleType();\n        switch (expr.getCode()) {\n        case Rem:\n            if (isConst(expr.getArguments().get(1), 1)) {\n                mc.report(\"RemOne\", 0, expr.getArguments().get(0));\n            }\n            break;\n        case Add:\n            checkSignedByte(expr, mc);\n            break;\n        case Or:\n            checkSignedByte(expr, mc);\n            // passthru\n        case Xor:\n            if (exprType == JvmType.Long || exprType == JvmType.Integer) {\n                Nodes.ifBinaryWithConst(expr, (child, constant) -> {\n                    if (constant instanceof Number && ((Number) constant).longValue() == 0\n                        && !Nodes.isCompoundAssignment(nc.getNode())\n                        && (nc.getParent() == null || !Nodes.isCompoundAssignment(nc.getParent().getNode()))) {\n                        mc.report(\"UselessOrWithZero\", 0, child, Roles.OPERATION.create(expr));\n                    }\n                });\n            }\n            break;\n        case And:\n            Nodes.ifBinaryWithConst(expr, (child, constant) -> {\n                if (constant instanceof Number) {\n                    long val = ((Number) constant).longValue();\n                    if (val == -1 && !Nodes.isCompoundAssignment(nc.getNode()))\n                        mc.report(\"UselessAndWithMinusOne\", 0, child, Roles.NUMBER.create((Number) constant));\n                    else if (val == 0)\n                        mc.report(\"UselessAndWithZero\", 0, child);\n                }\n            });\n            break;\n        case CmpGt:\n        case CmpLt: {\n            Expression bitAnd = Exprs.getChild(expr, expr.getCode() == AstCode.CmpGt ? 0 : 1);\n            Object zero = Nodes.getConstant(expr.getArguments().get(expr.getCode() == AstCode.CmpGt ? 1 : 0));\n            if (isIntegral(zero) && ((Number) zero).longValue() == 0 && bitAnd.getCode() == AstCode.And) {\n                Nodes.ifBinaryWithConst(bitAnd, (flags, mask) -> {\n                    if (isIntegral(mask)) {\n                        if (mask instanceof Integer && ((Integer) mask) < 0 || mask instanceof Long\n                            && ((Long) mask) < 0) {\n                            mc.report(\"BitCheckGreaterNegative\", 0, expr, Roles.NUMBER.create((Number) mask));\n                        } else {\n                            mc.report(\"BitCheckGreater\", 0, expr, Roles.NUMBER.create((Number) mask));\n                        }\n                    }\n                });\n            }\n            break;\n        }\n        case CmpEq:\n        case CmpNe:\n            Nodes.ifBinaryWithConst(expr, (child, outerConst) -> {\n                if (!isIntegral(outerConst))\n                    return;\n                if (child.getCode() != AstCode.And && child.getCode() != AstCode.Or)\n                    return;\n                long outerVal = ((Number) outerConst).longValue();\n                Nodes.ifBinaryWithConst(child, (grandChild, innerConst) -> {\n                    if (!isIntegral(innerConst)) \n                        return;\n                    long innerVal = ((Number) innerConst).longValue();\n                    if (child.getCode() == AstCode.And) {\n                        if ((outerVal & ~innerVal) != 0) {\n                            mc.report(\"CompareBitAndIncompatible\", 0, expr, AND_OPERAND.create(innerVal),\n                                COMPARED_TO.create(outerVal));\n                        }\n                    } else {\n                        if ((~outerVal & innerVal) != 0) {\n                            mc.report(\"CompareBitOrIncompatible\", 0, expr, OR_OPERAND.create(innerVal),\n                                COMPARED_TO.create(outerVal));\n                        }\n                    }\n                });\n            });\n            break;\n        case Shl:\n        case Shr:\n        case UShr: {\n            Object constant = Nodes.getConstant(expr.getArguments().get(1));\n            if (constant instanceof Integer) {\n                int bits = (int) constant;\n                if (bits < 0 || bits > 63 || (bits > 31 && exprType == JvmType.Integer)) {\n                    mc.report(\"BitShiftInvalidAmount\", 0, expr, Roles.NUMBER.create(bits), Roles.OPERATION.create(expr),\n                        Roles.MAX_VALUE.create(exprType == JvmType.Integer ? 31 : 63));\n                }\n            }\n        }\n        default:\n        }\n    }\n\n    private void checkSignedByte(Expression expr, MethodContext mc) {\n        JvmType type = expr.getInferredType().getSimpleType();\n        if (type != JvmType.Integer && type != JvmType.Long)\n            return;\n        if (Inf.BACKLINK.findUsages(expr).stream().allMatch(e -> e.getCode() == AstCode.I2B))\n            return;\n        if (Exprs.bothChildrenMatch(expr, BadMath::isByte, BadMath::isLow8BitsClear)) {\n            mc.report(expr.getCode() == AstCode.Add ? \"BitAddSignedByte\" : \"BitOrSignedByte\", 0, expr);\n        }\n    }\n\n    private static boolean isByte(Expression expr) {\n        if (expr.getCode() == AstCode.I2L)\n            return isByte(Exprs.getChild(expr, 0));\n        TypeReference type = expr.getInferredType();\n        return type != null && type.getInternalName().equals(\"B\");\n    }\n\n    private static boolean isLow8BitsClear(Expression arg) {\n        Object value = Nodes.getConstant(arg);\n        if (value instanceof Number) {\n            long num = ((Number) value).longValue();\n            return num != 0x100 && (num & 0xFF) == 0;\n        }\n        if (arg.getCode() == AstCode.Shl) {\n            Object shiftAmount = Nodes.getConstant(arg.getArguments().get(1));\n            return shiftAmount instanceof Number && (((Number) shiftAmount).intValue() & 0x1F) >= 8;\n        }\n        if (arg.getCode() == AstCode.And) {\n            Object leftOp = Nodes.getConstant(arg.getArguments().get(0));\n            Object rightOp = Nodes.getConstant(arg.getArguments().get(1));\n            if (leftOp instanceof Number && (((Number) leftOp).longValue() & 0xFF) == 0)\n                return true;\n            if (rightOp instanceof Number && (((Number) rightOp).longValue() & 0xFF) == 0)\n                return true;\n        }\n        return false;\n    }\n\n    private static boolean isConst(Expression expr, long wantedValue) {\n        Object constant = Nodes.getConstant(expr);\n        return isIntegral(constant) && ((Number) constant).longValue() == wantedValue;\n    }\n\n    private static boolean isIntegral(Object constant) {\n        return constant instanceof Integer || constant instanceof Long;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/BadMethodCalls.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.math.BigDecimal;\nimport java.util.Locale;\n\nimport com.strobel.assembler.metadata.BuiltinTypes;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.flow.etype.EType;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Role.StringRole;\nimport one.util.huntbugs.warning.Role.TypeRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"BadPractice\", name = \"SystemExit\", maxScore = 60)\n@WarningDefinition(category = \"BadPractice\", name = \"SystemGc\", maxScore = 50)\n@WarningDefinition(category = \"BadPractice\", name = \"SystemRunFinalizersOnExit\", maxScore = 60)\n@WarningDefinition(category = \"BadPractice\", name = \"ThreadStopThrowable\", maxScore = 60)\n@WarningDefinition(category = \"Performance\", name = \"URLBlockingMethod\", maxScore = 60)\n@WarningDefinition(category = \"Performance\", name = \"ConcurrentCollectionSize\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"UselessThread\", maxScore = 60)\n@WarningDefinition(category = \"Correctness\", name = \"BigDecimalConstructedFromDouble\", maxScore = 50)\n@WarningDefinition(category = \"Correctness\", name = \"BigDecimalConstructedFromInfiniteOrNaN\", maxScore = 70)\n@WarningDefinition(category = \"Correctness\", name = \"ArrayToString\", maxScore = 60)\n@WarningDefinition(category = \"Correctness\", name = \"CharArrayToString\", maxScore = 60)\n@WarningDefinition(category = \"Correctness\", name = \"StreamToString\", maxScore = 60)\n@WarningDefinition(category = \"Correctness\", name = \"ArrayHashCode\", maxScore = 60)\n@WarningDefinition(category = \"Correctness\", name = \"DoubleLongBitsToDoubleOnInt\", maxScore = 70)\n@WarningDefinition(category = \"Correctness\", name = \"ScheduledThreadPoolExecutorChangePoolSize\", maxScore = 70)\n@WarningDefinition(category = \"Correctness\", name = \"DateBadMonth\", maxScore = 70)\n@WarningDefinition(category = \"Correctness\", name = \"CollectionAddedToItself\", maxScore = 65)\n@WarningDefinition(category = \"RedundantCode\", name = \"NullCheckMethodForConstant\", maxScore = 65)\n@WarningDefinition(category = \"Correctness\", name = \"WrongArgumentOrder\", maxScore = 65)\npublic class BadMethodCalls {\n    private static final TypeDefinition STREAM_TYPE = Types.lookupJdkType(\"java/util/stream/Stream\");\n    private static final TypeDefinition CLD_TYPE = Types.lookupJdkType(\"java/util/concurrent/ConcurrentLinkedDeque\");\n    private static final TypeDefinition CLQ_TYPE = Types.lookupJdkType(\"java/util/concurrent/ConcurrentLinkedQueue\");\n    private static final TypeReference CHAR_ARRAY_TYPE = BuiltinTypes.Character.makeArrayType();\n    \n    private static final StringRole DOUBLE_NUMBER = StringRole.forName(\"DOUBLE_NUMBER\");\n    private static final StringRole BIGDECIMAL_NUMBER = StringRole.forName(\"BIGDECIMAL_NUMBER\");\n    private static final TypeRole ARG_TYPE = TypeRole.forName(\"ARG_TYPE\");\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression node, NodeChain nc, MethodContext ctx, MethodDefinition curMethod) {\n        if (Nodes.isInvoke(node) && node.getCode() != AstCode.InvokeDynamic) {\n            check(node, (MethodReference) node.getOperand(), nc, ctx, curMethod);\n        } else if (Nodes.isOp(node, AstCode.InitObject)) {\n            checkConstructor(node, (MethodReference) node.getOperand(), ctx);\n        }\n    }\n\n    private void checkConstructor(Expression node, MethodReference mr, MethodContext ctx) {\n        String typeName = mr.getDeclaringType().getInternalName();\n        String signature = mr.getSignature();\n        if (typeName.equals(\"java/lang/Thread\") && !signature.contains(\"Runnable\")) {\n            ctx.report(\"UselessThread\", 0, node);\n        } else if (typeName.equals(\"java/math/BigDecimal\") && signature.equals(\"(D)V\")) {\n            Object value = Nodes.getConstant(node.getArguments().get(0));\n            if (value instanceof Double) {\n                Double val = (Double) value;\n                if (val.isInfinite() || val.isNaN()) {\n                    ctx.report(\"BigDecimalConstructedFromInfiniteOrNaN\", 0, node, Roles.NUMBER.create(val));\n                } else {\n                    double arg = val.doubleValue();\n                    String dblString = value.toString();\n                    String bigDecimalString = new BigDecimal(arg).toString();\n                    boolean ok = dblString.equals(bigDecimalString) || dblString.equals(bigDecimalString + \".0\");\n\n                    if (!ok) {\n                        boolean scary = dblString.length() <= 8 && bigDecimalString.length() > 12 && dblString\n                                .toUpperCase().indexOf('E') == -1;\n                        if(bigDecimalString.length() > 40) {\n                            bigDecimalString = bigDecimalString.substring(0, 18)+\"...\"+bigDecimalString.substring(bigDecimalString.length()-18);\n                        }\n                        ctx.report(\"BigDecimalConstructedFromDouble\", scary ? 0 : 15, node, Roles.REPLACEMENT_METHOD\n                                .create(\"java/math/BigDecimal\", \"valueOf\", \"(D)Ljava/math/BigDecimal;\"), DOUBLE_NUMBER\n                                        .create(dblString), BIGDECIMAL_NUMBER.create(bigDecimalString));\n                    }\n                }\n            }\n        }\n    }\n\n    private void check(Expression node, MethodReference mr, NodeChain nc, MethodContext ctx,\n            MethodDefinition curMethod) {\n        String typeName = mr.getDeclaringType().getInternalName();\n        String name = mr.getName();\n        String signature = mr.getSignature();\n        if (typeName.equals(\"java/lang/System\") && name.equals(\"exit\")) {\n            String curName = curMethod.getName();\n            if (Methods.isMain(curMethod) || curName.equals(\"processWindowEvent\") || curName.startsWith(\"windowClos\"))\n                return;\n            int priority = 0;\n            curName = curName.toLowerCase(Locale.ENGLISH);\n            if (curName.indexOf(\"exit\") > -1 || curName.indexOf(\"crash\") > -1 || curName.indexOf(\"die\") > -1 || curName\n                    .indexOf(\"destroy\") > -1 || curName.indexOf(\"close\") > -1 || curName.indexOf(\"main\") > -1)\n                priority += 20;\n            if (curMethod.isStatic())\n                priority += 10;\n            String curType = curMethod.getDeclaringType().getInternalName();\n            if (curType.endsWith(\"Applet\") || curType.endsWith(\"App\") || curType.endsWith(\"Application\"))\n                priority += 10;\n            if (curMethod.getDeclaringType().getDeclaredMethods().stream().anyMatch(Methods::isMain))\n                priority += 20;\n            ctx.report(\"SystemExit\", priority, node);\n        } else if ((typeName.equals(\"java/lang/System\") || typeName.equals(\"java/lang/Runtime\")) && name.equals(\"gc\")\n            && signature.equals(\"()V\")) {\n            String curName = curMethod.getName();\n            if (Methods.isMain(curMethod) || curName.startsWith(\"test\"))\n                return;\n            if (nc.isInCatch(\"java/lang/OutOfMemoryError\"))\n                return;\n            if (Nodes.find(nc.getRoot(), BadMethodCalls::isTimeMeasure) != null)\n                return;\n            int priority = 0;\n            if (curName.toLowerCase(Locale.ENGLISH).contains(\"garbage\") || curName.toLowerCase(Locale.ENGLISH).contains(\n                \"memory\") || curName.startsWith(\"gc\") || curName.endsWith(\"gc\"))\n                priority += 10;\n            ctx.report(\"SystemGc\", priority, node);\n        } else if ((typeName.equals(\"java/lang/System\") || typeName.equals(\"java/lang/Runtime\")) && name.equals(\n            \"runFinalizersOnExit\")) {\n            ctx.report(\"SystemRunFinalizersOnExit\", 0, node);\n        } else if (typeName.equals(\"java/lang/Thread\") && name.equals(\"stop\") && signature.equals(\n            \"(Ljava/lang/Throwable;)V\")) {\n            ctx.report(\"ThreadStopThrowable\", 0, node);\n        } else if (typeName.equals(\"java/net/URL\") && (name.equals(\"equals\") || name.equals(\"hashCode\"))\n                && !fromFile(node)) {\n            ctx.report(\"URLBlockingMethod\", 0, node);\n        } else if (isToStringCall(typeName, name, signature)) {\n            Expression lastArg = node.getArguments().get(node.getArguments().size() - 1);\n            EType type = Inf.ETYPE.resolve(lastArg);\n            String wtype = null;\n            if(type.is(CHAR_ARRAY_TYPE, true).yes())\n                wtype = \"CharArrayToString\";\n            else if(type.isArray().yes())\n                wtype = \"ArrayToString\";\n            else if(type.is(STREAM_TYPE, false).yes())\n                wtype = \"StreamToString\";\n            if(wtype != null) {\n                ctx.report(wtype, 0, lastArg);\n            }\n        } else if (Methods.isHashCodeMethod(mr) || typeName.equals(\"java/util/Objects\") && name.equals(\"hashCode\")\n            && signature.equals(\"(Ljava/lang/Object;)I\")) {\n            Expression arg = node.getArguments().get(0);\n            EType type = Inf.ETYPE.resolve(arg);\n            if (type.isArray().yes()) {\n                ctx.report(\"ArrayHashCode\", 0, arg);\n            }\n        } else if (typeName.equals(\"java/util/Objects\") && name.equals(\"hash\") && signature.equals(\n            \"([Ljava/lang/Object;)I\")) {\n            Expression arg = node.getArguments().get(0);\n            if (arg.getCode() == AstCode.InitArray) {\n                for (Expression child : arg.getArguments()) {\n                    EType type = Inf.ETYPE.resolve(child);\n                    if (type.isArray().yes()) {\n                        ctx.report(\"ArrayHashCode\", 0, arg);\n                    }\n                }\n            }\n        } else if (typeName.equals(\"java/lang/Double\") && name.equals(\"longBitsToDouble\")) {\n            Expression arg = Exprs.getChild(node, 0);\n            if (arg.getCode() == AstCode.I2L) {\n                ctx.report(\"DoubleLongBitsToDoubleOnInt\", 0, arg, Roles.CALLED_METHOD.create(mr));\n            }\n        } else if (typeName.equals(\"java/util/concurrent/ThreadPoolExecutor\") && name.equals(\"setMaximumPoolSize\")) {\n            TypeReference type = ValuesFlow.reduceType(node.getArguments().get(0));\n            if (type.getInternalName().equals(\"java/util/concurrent/ScheduledThreadPoolExecutor\"))\n                ctx.report(\"ScheduledThreadPoolExecutorChangePoolSize\", 0, node, ARG_TYPE.create(type));\n        } else if ((typeName.equals(\"java/util/Date\") || typeName.equals(\"java/sql/Date\")) && signature.equals(\"(I)V\")\n            && name.equals(\"setMonth\")) {\n            Object month = Nodes.getConstant(node.getArguments().get(1));\n            if (month instanceof Integer) {\n                int m = (int) month;\n                if (m < 0 || m > 11) {\n                    ctx.report(\"DateBadMonth\", 0, node, Roles.NUMBER.create(m));\n                }\n            }\n        } else if (name.equals(\"add\") && mr.getErasedSignature().equals(\"(Ljava/lang/Object;)Z\") && Types.isCollection(\n            mr.getDeclaringType())) {\n            if (Nodes.isEquivalent(Exprs.getChild(node, 0), Exprs.getChild(node, 1))) {\n                ctx.report(\"CollectionAddedToItself\", 0, node);\n            }\n        } else if (node.getCode() == AstCode.InvokeStatic && (typeName.endsWith(\"/Assert\") && name.equals(\n            \"assertNotNull\") || typeName.equals(\"com/google/common/base/Preconditions\") && name.equals(\"checkNotNull\")\n            || typeName.equals(\"java/util/Objects\") && name.equals(\"requireNonNull\") || typeName.equals(\n                \"com/google/common/base/Strings\") && (name.equals(\"nullToEmpty\") || name.equals(\"emptyToNull\") || name\n                        .equals(\"isNullOrEmpty\")))) {\n            if (node.getArguments().size() == 1) {\n                Expression arg = node.getArguments().get(0);\n                Object constant = Nodes.getConstant(arg);\n                if (constant != null) {\n                    ctx.report(\"NullCheckMethodForConstant\", 0, node, Roles.CALLED_METHOD.create(mr));\n                }\n            }\n            if (node.getArguments().size() == 2) {\n                Object stringArg = null, objArg = null;\n                if (mr.getErasedSignature().startsWith(\"(Ljava/lang/Object;Ljava/lang/String;)\")) {\n                    objArg = Nodes.getConstant(node.getArguments().get(0));\n                    stringArg = Nodes.getConstant(node.getArguments().get(1));\n                } else if (mr.getErasedSignature().startsWith(\"(Ljava/lang/String;Ljava/lang/Object;)\")) {\n                    objArg = Nodes.getConstant(node.getArguments().get(1));\n                    stringArg = Nodes.getConstant(node.getArguments().get(0));\n                }\n                if (objArg instanceof String && !(stringArg instanceof String)) {\n                    ctx.report(\"WrongArgumentOrder\", 0, node.getArguments().get(0), Roles.CALLED_METHOD.create(mr),\n                        Roles.STRING.create((String) objArg));\n                }\n            }\n        } else if (name.equals(\"size\") && signature.equals(\"()I\") && (node.getCode() == AstCode.InvokeVirtual || node\n                .getCode() == AstCode.InvokeInterface)) {\n            EType eType = Inf.ETYPE.resolve(node.getArguments().get(0));\n            TypeReference target = null;\n            if (eType.is(CLD_TYPE, false).yes())\n                target = CLD_TYPE;\n            else if (eType.is(CLQ_TYPE, false).yes())\n                target = CLQ_TYPE;\n            if (target != null) {\n                ctx.report(\"ConcurrentCollectionSize\", 0, node, Roles.TARGET_TYPE.create(target));\n            }\n        }\n    }\n    \n    private boolean fromFile(Expression expr) {\n        for(Expression arg : expr.getArguments()) {\n            arg = ValuesFlow.getSource(arg);\n            if(arg.getCode() == AstCode.InvokeVirtual) {\n                MethodReference mr = (MethodReference)arg.getOperand();\n                if(mr.getDeclaringType().getInternalName().equals(\"java/io/File\"))\n                    return true;\n                if(mr.getDeclaringType().getInternalName().equals(\"java/net/URI\") &&\n                        fromFile(arg))\n                    return true;\n            }\n        }\n        return false;\n    }\n\n    private boolean isToStringCall(String typeName, String name, String signature) {\n        if (name.equals(\"toString\") && signature.equals(\"()Ljava/lang/String;\"))\n            return true;\n        if (name.equals(\"append\") && typeName.startsWith(\"java/lang/StringBu\") && signature.startsWith(\n            \"(Ljava/lang/Object;)Ljava/lang/StringBu\"))\n            return true;\n        if ((name.equals(\"print\") || name.equals(\"println\")) && signature.equals(\"(Ljava/lang/Object;)V\"))\n            return true;\n        return false;\n    }\n\n    private static boolean isTimeMeasure(Node node) {\n        if (!Nodes.isOp(node, AstCode.InvokeStatic))\n            return false;\n        MethodReference mr = (MethodReference) ((Expression) node).getOperand();\n        return mr.getName().equals(\"currentTimeMillis\") || mr.getName().equals(\"nanoTime\");\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/BadMethodReferences.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.DynamicCallSite;\nimport com.strobel.assembler.metadata.IMethodSignature;\nimport com.strobel.assembler.metadata.MethodHandle;\nimport com.strobel.assembler.metadata.MethodHandleType;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"MaxMinMethodReferenceForComparator\", maxScore=90)\npublic class BadMethodReferences {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS, minVersion=8)\n    public void visit(Expression expr, MethodContext mc) {\n        if(expr.getCode() == AstCode.InvokeDynamic) {\n            DynamicCallSite dcs = (DynamicCallSite)expr.getOperand();\n            MethodHandle actualHandle = Nodes.getMethodHandle(dcs);\n            if(actualHandle != null) {\n                IMethodSignature signature = dcs.getMethodType();\n                if(signature != null) {\n                    check(actualHandle, signature.getReturnType(), mc, expr);\n                }\n            }\n        }\n    }\n\n    private void check(MethodHandle handle, TypeReference functionalInterface, MethodContext mc, Expression expr) {\n        if(functionalInterface.getInternalName().equals(\"java/util/Comparator\") && handle.getHandleType() == MethodHandleType.InvokeStatic) {\n            MethodReference mr = handle.getMethod();\n            if((mr.getName().equals(\"min\") || mr.getName().equals(\"max\")) && mr.getDeclaringType().getPackageName().equals(\"java.lang\")) {\n                mc.report(\"MaxMinMethodReferenceForComparator\", 0, expr, Roles.METHOD_REFERENCE.create(mr));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/BadMonitorObject.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Role.TypeRole;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Multithreading\", name=\"SynchronizationOnBoolean\", maxScore=70)\n@WarningDefinition(category=\"Multithreading\", name=\"SynchronizationOnBoxedNumber\", maxScore=65)\n@WarningDefinition(category=\"Multithreading\", name=\"SynchronizationOnUnsharedBoxed\", maxScore=40)\npublic class BadMonitorObject {\n    private static final TypeRole MONITOR_TYPE = TypeRole.forName(\"MONITOR_TYPE\");\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if(expr.getCode() == AstCode.MonitorEnter) {\n            Expression arg = expr.getArguments().get(0);\n            if(arg.getCode() == AstCode.Load && ((Variable)arg.getOperand()).isGenerated())\n                arg = ValuesFlow.getSource(arg);\n            TypeReference type = arg.getInferredType();\n            if(type != null && Types.isBoxed(type)) {\n                String warningType;\n                if(arg.getCode() == AstCode.InitObject) {\n                    warningType = \"SynchronizationOnUnsharedBoxed\";\n                } else if(type.getInternalName().equals(\"java/lang/Boolean\")) {\n                    warningType = \"SynchronizationOnBoolean\";\n                } else {\n                    warningType = \"SynchronizationOnBoxedNumber\";\n                }\n                mc.report(warningType, 0, arg, MONITOR_TYPE.create(type), Roles.EXPRESSION.create(arg));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/CheckReturnValue.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"BadPractice\", name=\"ReturnValueOfRead\", maxScore=60)\n@WarningDefinition(category=\"BadPractice\", name=\"ReturnValueOfSkip\", maxScore=50)\npublic class CheckReturnValue {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc) {\n        if(nc.getNode() instanceof Expression)\n            return;\n        if(expr.getCode() == AstCode.InvokeVirtual || expr.getCode() == AstCode.InvokeInterface) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            boolean skipMethod = mr.getName().equals(\"skip\") && mr.getSignature().equals(\"(J)J\") ||\n                    mr.getName().equals(\"skipBytes\") && mr.getSignature().equals(\"(I)I\");\n            boolean readMethod = mr.getName().equals(\"read\") && (mr.getSignature().equals(\"([B)I\") ||\n                    mr.getSignature().equals(\"([BII)I\") || mr.getSignature().equals(\"([C)I\") ||\n                    mr.getSignature().equals(\"([CII)I\"));\n            if(skipMethod || readMethod) {\n                TypeReference type = ValuesFlow.reduceType(expr.getArguments().get(0));\n                if(type == null)\n                    type = mr.getDeclaringType();\n                if(!isInputStream(type))\n                    return;\n                if(skipMethod && Types.isInstance(type, \"javax/imageio/stream/ImageInputStream\"))\n                    return;\n                int priority = 0;\n                if(!Types.isInstance(type, \"java/io/BufferedInputStream\"))\n                    priority += 15;\n                mc.report(skipMethod ? \"ReturnValueOfSkip\" : \"ReturnValueOfRead\", priority, expr);\n            }\n        }\n    }\n\n    private boolean isInputStream(TypeReference type) {\n        return (Types.isInstance(type, \"java/io/InputStream\") && !Types.isInstance(type, \"java/io/ByteArrayInputStream\"))\n            || Types.isInstance(type, \"java/io/DataInput\") || Types.isInstance(type, \"java/io/Reader\");\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/CloneContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.stream.StreamSupport;\n\nimport com.strobel.assembler.ir.ConstantPool.NameAndTypeDescriptorEntry;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\n\n\n\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.db.Hierarchy.TypeHierarchy;\nimport one.util.huntbugs.registry.ClassContext;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.VisitOrder;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"BadPractice\", name=\"CloneableDoesNotImplementClone\", maxScore=40)\n@WarningDefinition(category=\"BadPractice\", name=\"CloneableNoSuperCall\", maxScore=50)\n@WarningDefinition(category=\"BadPractice\", name=\"NotCloneableHasClone\", maxScore=45)\npublic class CloneContract {\n    private boolean hasClone;\n    private boolean implementsCloneable;\n    private boolean isCloneable;\n    \n    @ClassVisitor(order=VisitOrder.BEFORE)\n    public boolean beforeClass(TypeDefinition td) {\n        if(td.isInterface())\n            return false;\n        implementsCloneable = td.getExplicitInterfaces().stream().map(TypeReference::getInternalName).anyMatch(\n            \"java/lang/Cloneable\"::equals);\n        isCloneable = implementsCloneable || Types.isInstance(td, \"java/lang/Cloneable\");\n        return true;\n    }\n\n    @AstVisitor(nodes=AstNodes.ROOT, methodName=\"clone\")\n    public void visitBody(MethodDefinition md, Block body, TypeDefinition td, MethodContext mc, TypeHierarchy th) {\n        if(!md.isSynthetic() && md.getErasedSignature().startsWith(\"()\")) {\n            hasClone = true;\n            boolean onlyThrows = body.getBody().size() == 1 && Nodes.isOp(body.getBody().get(0), AstCode.AThrow);\n            boolean deprecated = md.isDeprecated();\n            if(!isCloneable && !deprecated && !onlyThrows) {\n                int priority = referencesClone(td) ? 0 : 10;\n                if(td.isNonPublic() || !md.isPublic())\n                    priority += 20;\n                mc.report(\"NotCloneableHasClone\", priority, body);\n            }\n            if(isCloneable && !md.isFinal() && !td.isFinal() && !onlyThrows) {\n                boolean invokesSuperClone = Nodes.find(body, n -> {\n                    if(Nodes.isOp(n, AstCode.InvokeSpecial)) {\n                        MethodReference mr = (MethodReference) ((Expression)n).getOperand();\n                        if(mr.getName().equals(\"clone\") && mr.getErasedSignature().startsWith(\"()\"))\n                            return true;\n                    }\n                    return false;\n                }) != null;\n                if(!invokesSuperClone) {\n                    int priority = 0;\n                    if(th != null && !th.hasSubClasses()) {\n                        priority += 10;\n                        if(td.isNonPublic())\n                            priority += 10;\n                    }\n                    if(td.getDeclaredFields().isEmpty()) {\n                        priority += 10;\n                    }\n                    mc.report(\"CloneableNoSuperCall\", priority, body);\n                }\n            }\n        }\n    }\n    \n    @ClassVisitor(order=VisitOrder.AFTER)\n    public void afterClass(TypeDefinition td, ClassContext cc) {\n        if(implementsCloneable && !hasClone && !referencesClone(td)) {\n            cc.report(\"CloneableDoesNotImplementClone\", td.getDeclaredFields().isEmpty() ? 10 : 0);\n        }\n    }\n\n    private static boolean referencesClone(TypeDefinition td) {\n        return StreamSupport.stream(td.getConstantPool().spliterator(), false)\n            .anyMatch(entry -> entry instanceof NameAndTypeDescriptorEntry &&\n                ((NameAndTypeDescriptorEntry)entry).getName().equals(\"clone\") &&\n                ((NameAndTypeDescriptorEntry)entry).getType().startsWith(\"()\"));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/CompareContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author shustkost\n *\n */\n@WarningDefinition(category=\"BadPractice\", name=\"CompareReturnsMinValue\", maxScore=40)\npublic class CompareContract {\n    private static final Integer MIN_VALUE = Integer.valueOf(Integer.MIN_VALUE);\n\n    @MethodVisitor\n    public boolean check(MethodDefinition md, TypeDefinition td) {\n        if(md.isSynthetic())\n            return false;\n        if(md.getName().equals(\"compare\") && md.getParameters().size() == 2 && md.getReturnType().getSimpleType() == JvmType.Integer\n                && Types.isInstance(td, \"java/util/Comparator\"))\n            return true;\n        if(md.getName().equals(\"compareTo\") && md.getParameters().size() == 1 && md.getReturnType().getSimpleType() == JvmType.Integer\n                && Types.isInstance(td, \"java/lang/Comparable\"))\n            return true;\n        return false;\n    }\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if(MIN_VALUE.equals(Nodes.getConstant(expr))) {\n            if(Inf.BACKLINK.findTransitiveUsages(expr, true).anyMatch(e -> e.getCode() == AstCode.Return)) {\n                mc.report(\"CompareReturnsMinValue\", 0, expr);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/CompareUsage.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"BadPractice\", name = \"NegatingComparatorResult\", maxScore = 50)\n@WarningDefinition(category = \"Correctness\", name = \"ComparingComparatorResultWithNumber\", maxScore = 70)\npublic class CompareUsage {\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if (expr.getCode() == AstCode.Neg) {\n            Expression child = ValuesFlow.findFirst(Exprs.getChild(expr, 0), this::isCompare);\n            if (child != null) {\n                mc.report(\"NegatingComparatorResult\", 0, expr, Roles.CALLED_METHOD.create((MethodReference) child\n                        .getOperand()));\n            }\n        }\n        if (expr.getCode() == AstCode.CmpEq || expr.getCode() == AstCode.CmpNe) {\n            Nodes.ifBinaryWithConst(expr, (arg, constant) -> {\n                if (constant instanceof Integer && (int) constant != 0) {\n                    Expression child = ValuesFlow.findFirst(ValuesFlow.getSource(arg), this::isCompare);\n                    if (child != null) {\n                        mc.report(\"ComparingComparatorResultWithNumber\", 0, expr, Roles.CALLED_METHOD.create(\n                            (MemberReference) child.getOperand()), Roles.NUMBER.create((Number) constant));\n                    }\n                }\n            });\n        }\n    }\n\n    private boolean isCompare(Expression child) {\n        if (child.getCode() == AstCode.InvokeVirtual || child.getCode() == AstCode.InvokeSpecial\n            || child.getCode() == AstCode.InvokeInterface) {\n            MethodReference mr = (MethodReference) child.getOperand();\n            if (mr.getReturnType().getSimpleType() == JvmType.Integer\n                && ((mr.getName().equals(\"compare\") && Types.isInstance(mr.getDeclaringType(), \"java/util/Comparator\")) || mr\n                        .getName().equals(\"compareTo\"))) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/ConditionChain.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Role.LocationRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"RedundantCode\", name = \"SameConditions\", maxScore = 50)\n@WarningDefinition(category = \"RedundantCode\", name = \"SameConditionsExcluding\", maxScore = 75)\npublic class ConditionChain {\n    private static final LocationRole SAME_CONDITION = LocationRole.forName(\"SAME_CONDITION\");\n\n    @AstVisitor\n    public void visit(Node node, MethodContext mc, MethodDefinition md) {\n        if (node instanceof Condition) {\n            Condition cond = (Condition) node;\n            Expression expr = cond.getCondition();\n            check(expr, cond.getTrueBlock(), mc, false);\n            check(expr, cond.getFalseBlock(), mc, true);\n        }\n        if (node instanceof Expression) {\n            Expression expr = (Expression) node;\n            if (expr.getCode() == AstCode.LogicalAnd || expr.getCode() == AstCode.LogicalOr) {\n                check(expr.getArguments().get(0), expr.getArguments().get(1), expr.getCode(), mc);\n            }\n        }\n    }\n\n    private void check(Expression left, Expression right, AstCode condCode, MethodContext mc) {\n        if (Nodes.isEquivalent(left, right)) {\n            mc.report(\"SameConditions\", 0, left, SAME_CONDITION.create(mc, right), Roles.EXPRESSION.create(left));\n        } else if (left.getCode() == condCode && Nodes.isSideEffectFree(right)) {\n            if (Nodes.isSideEffectFree(left))\n                check(left.getArguments().get(0), right, condCode, mc);\n            check(left.getArguments().get(1), right, condCode, mc);\n        }\n    }\n\n    private void check(Expression expr, Block block, MethodContext mc, boolean excluding) {\n        List<Node> body = block.getBody();\n        if (body.isEmpty())\n            return;\n        Node node = body.get(0);\n        if (node instanceof Condition) {\n            Condition condNode = (Condition) node;\n            Expression condition = condNode.getCondition();\n            if (Nodes.isEquivalent(expr, condition)) {\n                int priority = 0;\n                if (Nodes.isEmptyOrBreak(condNode.getTrueBlock())) {\n                    excluding = !excluding;\n                    priority = 10;\n                }\n                mc.report(excluding ? \"SameConditionsExcluding\" : \"SameConditions\", priority, expr, Roles.EXPRESSION\n                        .create(condition), SAME_CONDITION.create(mc, condition));\n                return;\n            }\n            if (expr.getCode() == AstCode.LogicalAnd && !excluding && Nodes.isSideEffectFree(expr)) {\n                check(expr.getArguments().get(0), block, mc, excluding);\n                check(expr.getArguments().get(1), block, mc, excluding);\n            }\n            if (expr.getCode() == AstCode.LogicalOr && excluding && Nodes.isSideEffectFree(expr)) {\n                check(expr.getArguments().get(0), block, mc, excluding);\n                check(expr.getArguments().get(1), block, mc, excluding);\n            }\n            if (Nodes.isSideEffectFree(condition)) {\n                check(expr, condNode.getTrueBlock(), mc, excluding);\n                check(expr, condNode.getFalseBlock(), mc, excluding);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/CovariantArrays.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.BuiltinTypes;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.GenericParameter;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.db.Hierarchy;\nimport one.util.huntbugs.db.Hierarchy.TypeHierarchy;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"ContravariantArrayStore\", maxScore=60)\npublic class CovariantArrays {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc, Hierarchy h) {\n        if(expr.getCode() == AstCode.StoreElement) {\n            TypeReference arrayType = ValuesFlow.reduceType(Exprs.getChild(expr, 0));\n            TypeReference valueType = ValuesFlow.reduceType(Exprs.getChild(expr, 2));\n            if (arrayType == null || valueType == null || arrayType == BuiltinTypes.Null\n                || valueType == BuiltinTypes.Null || valueType.isPrimitive())\n                return;\n            valueType = toRawType(valueType);\n            if(Types.isObject(valueType))\n                return;\n            if(!arrayType.isArray())\n                return;\n            TypeReference arrayElementType = arrayType.getElementType();\n            arrayElementType = toRawType(arrayElementType);\n            if(Types.isObject(arrayElementType))\n                return;\n            if(!Types.isInstance(valueType, arrayElementType)) {\n                int priority = 0;\n                if(Types.isInstance(arrayElementType, valueType)) {\n                    priority += 20;\n                    if(allImplementationsDerivedFromSubclass(h, valueType, arrayElementType))\n                        return;\n                }\n                mc.report(\"ContravariantArrayStore\", priority, expr, Roles.ARRAY_TYPE.create(arrayType), Roles.VALUE_TYPE.create(valueType));\n            }\n        }\n    }\n\n    private TypeReference toRawType(TypeReference type) {\n        if(type.hasExtendsBound())\n            return toRawType(type.getExtendsBound());\n        if(type.hasSuperBound())\n            return BuiltinTypes.Object;\n        if(type instanceof GenericParameter)\n            return toRawType(type.getUnderlyingType());\n        if(!type.isGenericType())\n            return type;\n        try {\n            return type.getRawType();\n        } catch (UnsupportedOperationException e) {\n            return BuiltinTypes.Object;\n        }\n    }\n\n    private boolean allImplementationsDerivedFromSubclass(Hierarchy h, TypeReference superClass,\n            TypeReference subClass) {\n        TypeDefinition td = superClass.resolve();\n        if(td == null || (!td.isInterface() && !Flags.testAny(td.getFlags(), Flags.ABSTRACT)) )\n            return false;\n        for(TypeHierarchy th : h.get(td).getSubClasses()) {\n            if(subClass.getInternalName().equals(th.getInternalName()))\n                continue;\n            if(th.hasFlag(Flags.INTERFACE) || th.hasFlag(Flags.ABSTRACT))\n                continue;\n            TypeReference subType = td.getResolver().lookupType(th.getInternalName());\n            if(subType == null || Types.isInstance(subType, subClass))\n                continue;\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/DeadLocalStore.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.CatchBlock;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"RedundantCode\", name=\"DeadStoreInReturn\", maxScore=50)\n@WarningDefinition(category=\"RedundantCode\", name=\"DeadIncrementInReturn\", maxScore=60)\n@WarningDefinition(category=\"RedundantCode\", name=\"DeadIncrementInAssignment\", maxScore=60)\n@WarningDefinition(category=\"RedundantCode\", name=\"DeadParameterStore\", maxScore=60)\n@WarningDefinition(category=\"RedundantCode\", name=\"DeadLocalStore\", maxScore=50)\n@WarningDefinition(category=\"RedundantCode\", name=\"UnusedLocalVariable\", maxScore=35)\npublic class DeadLocalStore {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc, MethodDefinition md, TypeDefinition td) {\n        if(expr.getCode() == AstCode.Return && expr.getArguments().size() == 1) {\n            Expression arg = expr.getArguments().get(0);\n            if(arg.getCode() == AstCode.Store) {\n                mc.report(\"DeadStoreInReturn\", 0, arg);\n            } else if(arg.getCode() == AstCode.PostIncrement) {\n                Expression var = arg.getArguments().get(0);\n                if(var.getOperand() instanceof Variable)\n                    mc.report(\"DeadIncrementInReturn\", 0, var);\n            }\n        }\n        if(expr.getCode() == AstCode.Store) {\n            Variable var = (Variable) expr.getOperand();\n            if(var.isGenerated())\n                return;\n            Expression child = expr.getArguments().get(0);\n            if(child.getCode() == AstCode.PostIncrement) {\n                Expression load = child.getArguments().get(0);\n                if(load.getCode() == AstCode.Load && var.equals(load.getOperand()) && Integer.valueOf(1).equals(child.getOperand())) {\n                    mc.report(\"DeadIncrementInAssignment\", 0, expr, Roles.EXPRESSION.create(var.getName() + \" = \" + var\n                            .getName() + \"++\"));\n                }\n            }\n            List<Expression> args = child.getCode() == AstCode.TernaryOp ? child.getArguments().subList(1, 3)\n                    : Collections.singletonList(child);\n            for(Expression arg : args) {\n                // exclude AstCode.Store (chaining assignment) as procyon sometimes reorders locals assignments\n                if (mc.isAnnotated() && ValuesFlow.getSource(arg) == arg && arg.getCode() != AstCode.Store\n                    && arg.getCode() != AstCode.PutField && arg.getCode() != AstCode.PutStatic) {\n                    Set<Expression> usages = Inf.BACKLINK.findUsages(arg);\n                    if(usages.size() == 1 && usages.iterator().next() == expr) {\n                        Set<Expression> storeUsages = Inf.BACKLINK.findUsages(expr);\n                        if(storeUsages.isEmpty()) {\n                            if(mc.getCFG() != null && !mc.getCFG().isInCFG(expr)) {\n                                // Was removed from CFG (probably inlined)\n                                return;\n                            }\n                            if(nc.getNode() instanceof CatchBlock && nc.getNode().getChildren().get(0) == expr\n                                    && ((CatchBlock)nc.getNode()).getCaughtTypes().size() > 1) {\n                                // Exception variable in multi-catch block\n                                return;\n                            }\n                            // autogenerated by javacc\n                            if (md.getName().equals(\"adjustBeginLineColumn\") && td.getSimpleName().equals(\n                                \"SimpleCharStream\") || md.getName().startsWith(\"jj\") && td.getSimpleName().endsWith(\n                                    \"TokenManager\")) {\n                                return;\n                            }\n                            // autogenerated by antlr\n                            if(Types.is(td.getBaseType(), \"org/antlr/runtime/Lexer\") || Types.isInstance(td, \"org/antlr/runtime/Parser\")) {\n                                return;\n                            }\n                            // autogenerated by protocol buffers\n                            if(Types.is(td.getBaseType(), \"com/google/protobuf/GeneratedMessage$Builder\")) {\n                                return;\n                            }\n                            // autogenerated by jflex\n                            if (var.getName().equals(\"offset\") && md.getName().startsWith(\"zz\") && (td.getSimpleName()\n                                    .endsWith(\"Lexer\") || td.getSimpleName().contains(\"Tokenizer\"))) {\n                                return;\n                            }\n                            int priority = 0;\n                            Block root = nc.getRoot();\n                            if(arg.getCode() == AstCode.LdC && !var.isParameter()) {\n                                Object val = arg.getOperand();\n                                TypeReference tr = arg.getInferredType();\n                                if(tr != null && (tr.isPrimitive() || Types.isString(tr))) {\n                                    // probably final constant var which is inlined when used\n                                    if (Nodes.find(root, n -> n != expr && n instanceof Expression\n                                            && ((Expression) n).getOperand() == var) == null) {\n                                        if(Nodes.find(root, n -> n != arg && n instanceof Expression\n                                            && val.equals(((Expression) n).getOperand())) != null)\n                                            return;\n                                        // If constant value is not used explicitly, it's still possible that it's inlined\n                                        // inside other constant expression, thus lower the priority\n                                        priority += 20;\n                                    }\n                                }\n                            }\n                            if(var.isParameter()) {\n                                mc.report(\"DeadParameterStore\", priority, expr);\n                            } else {\n                                boolean unusedLocal = Nodes.find(root, n -> n instanceof Expression\n                                    && ((Expression) n).getOperand() == var && ((Expression) n).getCode() != AstCode.Store) == null;\n                                String type;\n                                if (!unusedLocal) {\n                                    type = \"DeadLocalStore\";\n                                    if(arg.getCode() == AstCode.AConstNull) {\n                                        priority += 20;\n                                        if(mc.mayTerminateImplicitly(arg))\n                                            return;\n                                    } else if(arg.getCode() == AstCode.LdC) {\n                                        if (arg.getOperand() instanceof Number\n                                            && ((Number) arg.getOperand()).doubleValue() == 0.0)\n                                            priority += 20;\n                                        else if (\"\".equals(arg.getOperand()) || Integer.valueOf(1).equals(arg.getOperand())\n                                                || Integer.valueOf(-1).equals(arg.getOperand()))\n                                            priority += 10;\n                                        if(mc.mayTerminateImplicitly(arg))\n                                            return;\n                                    }\n                                } else {\n                                    type = \"UnusedLocalVariable\";\n                                }\n                                mc.report(type, priority, expr);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/DroppedExceptionObject.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"DroppedException\", maxScore=60)\npublic class DroppedExceptionObject {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc) {\n        if (expr.getCode() == AstCode.InitObject || expr.getCode() == AstCode.InvokeSpecial\n            && expr.getArguments().get(0).getCode() == AstCode.__New) { // Probably procyon bug: invokespecial(__new) is not collapsed to InitObject \n            MethodReference mr = (MethodReference) expr.getOperand();\n            if(Types.isInstance(mr.getDeclaringType(), \"java/lang/Throwable\") ||\n                    Types.isInstance(mr.getDeclaringType(), \"java/lang/Exception\")) {\n                if(nc.getNode() instanceof Block) {\n                    mc.report(\"DroppedException\", 0, expr, Roles.EXCEPTION.create(mr.getDeclaringType()));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/DubiousCatch.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.CatchBlock;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"BadPractice\", name=\"CatchIllegalMonitorStateException\", maxScore=50)\n@WarningDefinition(category=\"BadPractice\", name=\"CatchConcurrentModificationException\", maxScore=50)\npublic class DubiousCatch {\n    private static final Map<String, String> EXCEPTION_TO_WARNING = new HashMap<>();\n    \n    static {\n        EXCEPTION_TO_WARNING.put(\"java/lang/IllegalMonitorStateException\", \"CatchIllegalMonitorStateException\");\n        EXCEPTION_TO_WARNING.put(\"java/util/ConcurrentModificationException\", \"CatchConcurrentModificationException\");\n    }\n    \n    @AstVisitor\n    public void visit(Node node, MethodContext mc) {\n        if(node instanceof CatchBlock) {\n            CatchBlock block = (CatchBlock)node;\n            List<TypeReference> exceptions = block.getCaughtTypes();\n            if(exceptions.isEmpty() && block.getExceptionType() != null)\n                exceptions = Collections.singletonList(block.getExceptionType());\n            for(TypeReference type : exceptions) {\n                String warningType = EXCEPTION_TO_WARNING.get(type.getInternalName());\n                if(warningType != null) {\n                    mc.report(warningType, 0, node, Roles.EXCEPTION.create(type));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/DuplicateAssignment.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.LocationRole;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"RedundantCode\", name=\"FieldDoubleAssignment\", maxScore=65)\npublic class DuplicateAssignment {\n    private static final LocationRole DUPLICATE_ASSIGNMENT_AT = LocationRole.forName(\"DUPLICATE_ASSIGNMENT_AT\");\n    \n    @AstVisitor\n    public void visit(Node node, NodeChain nc, MethodDefinition md, MethodContext mc) {\n        if(node instanceof Block) {\n            List<Node> body = ((Block) node).getBody();\n            for(int i=0; i<body.size()-1; i++) {\n                Node n = body.get(i);\n                if(!(n instanceof Expression))\n                    continue;\n                Expression e = (Expression) n;\n                if(e.getCode() == AstCode.PutField) {\n                    Expression receiver = Exprs.getChildNoSpecial(e, 0);\n                    FieldReference fr = (FieldReference) e.getOperand();\n                    for(int j=i+1; j<body.size(); j++) {\n                        Node n2 = body.get(j);\n                        if(!(n2 instanceof Expression))\n                            break;\n                        Expression e2 = (Expression) n2;\n                        if(e2.getCode() == AstCode.Store || e2.getCode() == AstCode.StoreElement) {\n                            if(Exprs.findExpression(e2, e1 -> !Nodes.isSideEffectFree(e1)) != null)\n                                break;\n                            continue;\n                        }\n                        if(e2.getCode() != AstCode.PutField)\n                            break;\n                        if(!Nodes.isSideEffectFree(e2.getArguments().get(1)))\n                            break;\n                        if (Exprs.findExpression(Exprs.getChild(e2, 1),\n                            ex -> ex.getCode() == AstCode.GetField\n                                && fr.isEquivalentTo((FieldReference) ex.getOperand())\n                                && Nodes.isEquivalent(Exprs.getChildNoSpecial(ex, 0), receiver)) != null)\n                            break;\n                        Expression receiver2 = Exprs.getChildNoSpecial(e2, 0);\n                        FieldReference fr2 = (FieldReference) e2.getOperand();\n                        if(fr.isEquivalentTo(fr2) && Nodes.isEquivalent(receiver, receiver2)) {\n                            int priority = 0;\n                            if(md.isConstructor() && nc == null && Exprs.isThis(receiver2))\n                                continue;\n                            mc.report(\"FieldDoubleAssignment\", priority, e, DUPLICATE_ASSIGNMENT_AT.create(mc\n                                    .getLocation(e2)));\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/EasyMockProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.stream.StreamSupport;\n\nimport com.strobel.assembler.ir.ConstantPool.TypeInfoEntry;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"RedundantCode\", name = \"UselessEasyMockCall\", maxScore = 50)\npublic class EasyMockProblems {\n    @ClassVisitor\n    public boolean check(TypeDefinition td) {\n        return StreamSupport.stream(td.getConstantPool().spliterator(), false).anyMatch(\n            entry -> entry instanceof TypeInfoEntry && ((TypeInfoEntry) entry).getName().startsWith(\"org/easymock/\"));\n    }\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if (expr.getCode() == AstCode.InvokeStatic) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if (mr.getDeclaringType().getInternalName().equals(\"org/easymock/EasyMock\")\n                && (mr.getName().equals(\"replay\") || mr.getName().equals(\"verify\") || mr.getName().startsWith(\"reset\"))\n                && mr.getErasedSignature().equals(\"([Ljava/lang/Object;)V\")) {\n                Expression child = Exprs.getChild(expr, 0);\n                if (child.getCode() == AstCode.InitArray && child.getArguments().isEmpty()\n                    || child.getCode() == AstCode.NewArray\n                    && Integer.valueOf(0).equals(Nodes.getConstant(child.getArguments().get(0)))) {\n                    mc.report(\"UselessEasyMockCall\", 0, expr);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/EmptySync.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\n\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.TryCatchBlock;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Multithreading\", name=\"EmptySynchronizeBlock\", maxScore=50)\npublic class EmptySync {\n    @AstVisitor\n    public void visit(Node node, MethodContext mc) {\n        if(node instanceof Block) {\n            List<Node> body = ((Block) node).getBody();\n            for(int i=0; i<body.size()-1; i++) {\n                if(Nodes.isOp(body.get(i), AstCode.MonitorEnter)) {\n                    Node next = body.get(i+1);\n                    if(Nodes.isOp(next, AstCode.MonitorExit))\n                        // ECJ scenario: simply monitorenter/monitorexit\n                        mc.report(\"EmptySynchronizeBlock\", 0, body.get(i));\n                    else if(next instanceof TryCatchBlock) {\n                        TryCatchBlock tryCatch = (TryCatchBlock)next;\n                        if(Nodes.isSynchorizedBlock(tryCatch) && tryCatch.getTryBlock().getBody().isEmpty()) {\n                            // JAVAC scenario: try-catch added\n                            mc.report(\"EmptySynchronizeBlock\", 0, body.get(i));\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/EqualsContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.ClassContext;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.VisitOrder;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\nimport one.util.huntbugs.warning.Role.MemberRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"BadPractice\", name = \"EqualsReturnsTrue\", maxScore = 50)\n@WarningDefinition(category = \"BadPractice\", name = \"EqualsReturnsFalse\", maxScore = 50)\n@WarningDefinition(category = \"BadPractice\", name = \"EqualsClassNames\", maxScore = 45)\n@WarningDefinition(category = \"BadPractice\", name = \"EqualsOther\", maxScore = 40)\n@WarningDefinition(category = \"BadPractice\", name = \"EqualsSelf\", maxScore = 50)\n@WarningDefinition(category = \"BadPractice\", name = \"EqualsEnum\", maxScore = 60)\n@WarningDefinition(category = \"BadPractice\", name = \"HashCodeObjectEquals\", maxScore = 45)\n@WarningDefinition(category = \"BadPractice\", name = \"HashCodeNoEquals\", maxScore = 55)\n@WarningDefinition(category = \"BadPractice\", name = \"EqualsObjectHashCode\", maxScore = 45)\n@WarningDefinition(category = \"BadPractice\", name = \"EqualsNoHashCode\", maxScore = 55)\n@WarningDefinition(category = \"Correctness\", name = \"EqualsSuspiciousFieldComparison\", maxScore = 60)\npublic class EqualsContract {\n    private static final MemberRole NORMAL_EQUALS = MemberRole.forName(\"NORMAL_EQUALS\");\n    private static final MemberRole OTHER_FIELD = MemberRole.forName(\"OTHER_FIELD\");\n\n    boolean alwaysFalse = false;\n    boolean instanceCheckingEquals = false;\n\n    @AstVisitor(nodes = AstNodes.ROOT, methodName = \"equals\", methodSignature = \"(Ljava/lang/Object;)Z\")\n    public void visitEquals(Block body, MethodContext mc, TypeDefinition td) {\n        List<Node> list = body.getBody();\n        if (list.size() == 1) {\n            Node node = list.get(0);\n            if (Nodes.isOp(node, AstCode.Return)) {\n                Expression retVal = ((Expression) node).getArguments().get(0);\n                Object constant = Nodes.getConstant(retVal);\n                int priority = getPriority(td);\n                if (((Integer) 1).equals(constant))\n                    mc.report(\"EqualsReturnsTrue\", priority, node);\n                else if (((Integer) 0).equals(constant)) {\n                    mc.report(\"EqualsReturnsFalse\", priority, node);\n                    alwaysFalse = true;\n                }\n                if(isInstanceOfAndSuperEquals(retVal))\n                    instanceCheckingEquals = true;\n                // Check the following pattern:\n                // public boolean equals(Object x) {\n                //     return this == x || x instanceof XYZ && super.equals(x);\n                // }\n                if(retVal.getCode() == AstCode.LogicalOr && isSelfCheck(retVal.getArguments().get(0)) && isInstanceOfAndSuperEquals(retVal.getArguments().get(1))) {\n                    instanceCheckingEquals = true;\n                }\n            }\n        }\n        if (list.size() == 2) {\n            // Check the following pattern:\n            // public boolean equals(Object x) {\n            //     if(!(x instanceof XYZ)) return false;\n            //     return super.equals(x);\n            // }\n            Node node1 = list.get(0);\n            Node node2 = list.get(1);\n            if (node1 instanceof Condition) {\n                Expression cond = ((Condition) node1).getCondition();\n                if (cond.getCode() == AstCode.LogicalNot && cond.getArguments().get(0).getCode() == AstCode.InstanceOf) {\n                    if (Nodes.isOp(node2, AstCode.Return)) {\n                        Expression child = ((Expression) node2).getArguments().get(0);\n                        if (child.getCode() == AstCode.InvokeSpecial\n                            && Methods.isEqualsMethod((MethodReference) child.getOperand())) {\n                            instanceCheckingEquals = true;\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * @param expr expression to check\n     * @return true if expression is like x instanceof XYZ && super.equals(x) \n     */\n    private static boolean isInstanceOfAndSuperEquals(Expression expr) {\n        boolean isInstanceOfAndSuperEquals = false;\n        if (expr.getCode() == AstCode.LogicalAnd) {\n            Expression left = expr.getArguments().get(0);\n            Expression right = expr.getArguments().get(1);\n            if (left.getCode() == AstCode.InstanceOf && right.getCode() == AstCode.InvokeSpecial\n                && Methods.isEqualsMethod((MethodReference) right.getOperand())) {\n                isInstanceOfAndSuperEquals = true;\n            }\n        }\n        return isInstanceOfAndSuperEquals;\n    }\n    \n    /**\n     * @param expr expression to check\n     * @return true if expression is like this == arg \n     */\n    private static boolean isSelfCheck(Expression expr) {\n        if(expr.getCode() == AstCode.CmpEq) {\n            Expression left = expr.getArguments().get(0);\n            Expression right = expr.getArguments().get(1);\n            return Exprs.isThis(left) && right.getCode() == AstCode.Load ||\n                    Exprs.isThis(right) && left.getCode() == AstCode.Load;\n        }\n        return false;\n    }\n\n    @ClassVisitor(order = VisitOrder.AFTER)\n    public void visitClass(TypeDefinition td, ClassContext cc) {\n        MethodDefinition equalsSelf = null;\n        MethodDefinition equalsObject = null;\n        MethodDefinition equalsOther = null;\n        MethodDefinition hashCode = null;\n        MethodDefinition superEquals = null;\n        MethodDefinition superHashCode = null;\n        int basePriority = td.isPrivate() || td.isPackagePrivate() ? 20 : 0;\n        for (MethodDefinition md : td.getDeclaredMethods()) {\n            if (!md.isStatic() && md.getName().equals(\"equals\")\n                && md.getReturnType().getSimpleType() == JvmType.Boolean && md.getParameters().size() == 1) {\n                TypeReference type = md.getParameters().get(0).getParameterType();\n                if (Types.isObject(type)) {\n                    equalsObject = md;\n                } else if (type.isEquivalentTo(td)) {\n                    equalsSelf = md;\n                } else if (!type.isPrimitive()) {\n                    equalsOther = md;\n                }\n            }\n            if (!md.isStatic() && !md.isBridgeMethod() && Methods.isHashCodeMethod(md)) {\n                hashCode = md;\n            }\n        }\n        if (equalsObject == null) {\n            superEquals = Methods.findSuperMethod(td, new MemberInfo(td.getInternalName(), \"equals\",\n                    \"(Ljava/lang/Object;)Z\"));\n        }\n        if (hashCode == null) {\n            superHashCode = Methods.findSuperMethod(td, new MemberInfo(td.getInternalName(), \"hashCode\", \"()I\"));\n        }\n        if (hashCode != null && !hashCode.isAbstract() && equalsObject == null && equalsSelf == null) {\n            if (superEquals == null || Types.isObject(superEquals.getDeclaringType())) {\n                cc.report(\"HashCodeObjectEquals\", basePriority, Roles.METHOD.create(hashCode));\n            } else if (!superEquals.isFinal()) {\n                cc.report(\"HashCodeNoEquals\", basePriority, Roles.METHOD.create(hashCode), Roles.SUPER_METHOD\n                        .create(superEquals));\n            }\n        }\n        if (hashCode == null && equalsObject != null && !alwaysFalse && !instanceCheckingEquals) {\n            if (superHashCode == null || Types.isObject(superHashCode.getDeclaringType())) {\n                int priority = basePriority;\n                if (Flags.testAny(td.getFlags(), Flags.ABSTRACT)) {\n                    priority += 10;\n                }\n                cc.report(\"EqualsObjectHashCode\", priority, Roles.METHOD.create(equalsObject));\n            } else if (!superHashCode.getDeclaringType().getInternalName().startsWith(\"java/util/Abstract\")\n                && !Methods.isThrower(superHashCode)) {\n                int priority = basePriority;\n                if (Flags.testAny(td.getFlags(), Flags.ABSTRACT)) {\n                    priority += 10;\n                }\n                if (td.getDeclaredFields().isEmpty())\n                    priority += 10;\n                cc.report(\"EqualsNoHashCode\", priority, Roles.METHOD.create(equalsObject), Roles.SUPER_METHOD\n                        .create(superHashCode));\n            }\n        }\n        if (equalsObject == null && equalsSelf == null && equalsOther != null) {\n            cc.report(\"EqualsOther\", getPriority(td) + getPriority(equalsOther), Roles.METHOD.create(equalsOther),\n                NORMAL_EQUALS.create(\"java/lang/Object\", \"equals\", \"(Ljava/lang/Object;)Z\"));\n        } else if (equalsObject == null && equalsSelf != null) {\n            if (Types.isInstance(td, \"java/lang/Enum\"))\n                cc.report(\"EqualsEnum\", getPriority(td) + getPriority(equalsSelf), Roles.METHOD.create(equalsSelf),\n                    NORMAL_EQUALS.create(\"java/lang/Enum\", \"equals\", \"(Ljava/lang/Object;)Z\"));\n            else\n                cc.report(\"EqualsSelf\", getPriority(td) + getPriority(equalsSelf), Roles.METHOD.create(equalsSelf),\n                    NORMAL_EQUALS.create(\"java/lang/Object\", \"equals\", \"(Ljava/lang/Object;)Z\"));\n        }\n    }\n\n    private static int getPriority(TypeDefinition td) {\n        int priority = 0;\n        if (td.isNonPublic())\n            priority += 30;\n        if (td.isFinal())\n            priority += 5;\n        return priority;\n    }\n\n    private static int getPriority(MethodDefinition md) {\n        int priority = 0;\n        if (md.isProtected())\n            priority += 10;\n        else if (md.isPackagePrivate() || md.isPrivate())\n            priority += 20;\n        return priority;\n    }\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS, methodName = \"equals\", methodSignature = \"(Ljava/lang/Object;)Z\")\n    public void visitEqualsExpression(Expression expr, MethodContext mc) {\n        if(isComparison(expr)) {\n            Expression left = expr.getArguments().get(0);\n            Expression right = expr.getArguments().get(1);\n            if(left.getCode() == AstCode.GetField && right.getCode() == AstCode.GetField) {\n                FieldReference lfr = ((FieldReference) left.getOperand());\n                FieldReference rfr = ((FieldReference) right.getOperand());\n                if(!lfr.isEquivalentTo(rfr)) {\n                    lfr = lfr.resolve();\n                    rfr = rfr.resolve();\n                    // TODO: do not warn about comparisons like if(a == o.a && b == o.b || a == o.b && b == o.a)\n                    if(lfr != null && rfr != null && !lfr.isEquivalentTo(rfr)) {\n                        if (Exprs.bothMatch(Exprs.getChild(left, 0), Exprs.getChild(right, 0), Exprs::isThis,\n                            EqualsContract::isParameter)) {\n                            mc.report(\"EqualsSuspiciousFieldComparison\", 0, left, OTHER_FIELD.create(rfr));\n                        }\n                    }\n                }\n            }\n            if (expr.getCode() == AstCode.InvokeVirtual && Methods.isEqualsMethod((MethodReference) expr.getOperand())) {\n                checkGetName(expr, mc, left);\n                checkGetName(expr, mc, right);\n            }\n        }\n    }\n    \n    private static boolean isParameter(Expression expr) {\n        if(expr.getCode() == AstCode.CheckCast) {\n            expr = Exprs.getChild(expr, 0);\n        }\n        return Exprs.isParameter(expr) && !Exprs.isThis(expr);\n    }\n\n    private static boolean isComparison(Expression expr) {\n        switch(expr.getCode()) {\n        case CmpEq:\n        case CmpNe:\n            return true;\n        case InvokeVirtual:\n            return Methods.isEqualsMethod((MethodReference) expr.getOperand());\n        case InvokeStatic: {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            TypeReference tr = mr.getDeclaringType();\n            return (mr.getName().equals(\"equals\") || mr.getName().equals(\"deepEquals\")) &&\n                    (Types.is(tr, Arrays.class) || Types.is(tr, Objects.class));\n        }\n        default:\n            return false;\n        }\n    }\n\n    private void checkGetName(Expression expr, MethodContext mc, Expression getName) {\n        if (isClassGetName(getName)) {\n            Expression getClass = Exprs.getChild(getName, 0);\n            if (isGetClass(getClass)) {\n                Expression source = Exprs.getChild(getClass, 0);\n                if (Exprs.isThis(source) || Exprs.isParameter(source)) {\n                    mc.report(\"EqualsClassNames\", 0, expr);\n                }\n            }\n        }\n    }\n\n    private static boolean isClassGetName(Expression expr) {\n        if (expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if (mr.getName().equals(\"getName\") && Types.is(mr.getDeclaringType(), Class.class))\n                return true;\n        }\n        return false;\n    }\n\n    private static boolean isGetClass(Expression expr) {\n        if (expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            return Methods.isGetClass(mr);\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/ExceptionalExpression.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Exceptional;\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation.TypeInfo;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"ExceptionalExpression\", maxScore=80)\npublic class ExceptionalExpression {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc) {\n        Object constValue = Inf.CONST.getValue(expr);\n        if(constValue instanceof Exceptional) {\n            int priority = 0;\n            TypeInfo exc = ((Exceptional) constValue).getType();\n            if(nc.isInTry(exc.getTypeName()))\n                priority += 30;\n            mc.report(\"ExceptionalExpression\", priority, expr, Roles.EXPRESSION.create(expr), Roles.EXCEPTION\n                    .create(exc));\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/ExclusiveConditions.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Equi;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.StringRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"AndEqualsAlwaysFalse\", maxScore = 85)\n@WarningDefinition(category = \"Correctness\", name = \"OrNotEqualsAlwaysTrue\", maxScore = 70)\npublic class ExclusiveConditions {\n    private static final StringRole CONST1 = StringRole.forName(\"CONST1\");\n    private static final StringRole CONST2 = StringRole.forName(\"CONST2\");\n\n    Set<Expression> reported;\n\n    @MethodVisitor\n    public void init() {\n        reported = new HashSet<>();\n    }\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if (expr.getCode() == AstCode.LogicalAnd) {\n            if (Nodes.isSideEffectFree(expr)) {\n                Expression left = expr.getArguments().get(0);\n                Expression right = expr.getArguments().get(1);\n                if (isEquality(left)) {\n                    checkEqual(left, right, mc);\n                } else if (isEquality(right)) {\n                    checkEqual(right, left, mc);\n                }\n            }\n        }\n        if (expr.getCode() == AstCode.LogicalOr) {\n            if (Nodes.isSideEffectFree(expr)) {\n                Expression left = expr.getArguments().get(0);\n                Expression right = expr.getArguments().get(1);\n                if (isNonEquality(left)) {\n                    checkNonEqual(left, right, mc);\n                } else if (isNonEquality(right)) {\n                    checkNonEqual(right, left, mc);\n                }\n            }\n        }\n    }\n\n    private boolean isEquality(Expression expr) {\n        if (expr.getCode() == AstCode.CmpEq)\n            return true;\n        if (expr.getCode() == AstCode.InvokeVirtual) {\n            return Methods.isEqualsMethod((MethodReference) expr.getOperand());\n        }\n        return false;\n    }\n\n    private boolean isNonEquality(Expression expr) {\n        if (expr.getCode() == AstCode.CmpNe)\n            return true;\n        if (expr.getCode() == AstCode.Neg) {\n            Expression arg = expr.getArguments().get(0);\n            if (arg.getCode() == AstCode.InvokeVirtual) {\n                return Methods.isEqualsMethod((MethodReference) arg.getOperand());\n            }\n        }\n        return false;\n    }\n\n    private void checkEqual(Expression equality, Expression other, MethodContext mc) {\n        Nodes.ifBinaryWithConst(equality, (arg, constant) -> {\n            if (isEquality(other)) {\n                Expression left = other.getArguments().get(0);\n                Expression right = other.getArguments().get(1);\n                Expression arg2 = null;\n                Object constant2 = null;\n                if(Equi.equiExpressions(arg, left)) {\n                    constant2 = Nodes.getConstant(right);\n                    arg2 = left;\n                } else if(Equi.equiExpressions(arg, right)) {\n                    constant2 = Nodes.getConstant(left);\n                    arg2 = right;\n                }\n                if(constant2 != null && !Objects.equals(constant, constant2)) {\n                    // non-short-circuit logic is intended\n                    if (reported.add(arg) & reported.add(arg2)) {\n                        mc.report(\"AndEqualsAlwaysFalse\", 0, arg, CONST1.createFromConst(constant), CONST2\n                                .createFromConst(constant2));\n                    }\n                }\n            }\n            if (other.getCode() == AstCode.LogicalAnd) {\n                checkEqual(equality, other.getArguments().get(0), mc);\n                checkEqual(equality, other.getArguments().get(1), mc);\n            }\n        });\n    }\n\n    private void checkNonEqual(Expression equality, Expression other, MethodContext mc) {\n        Nodes.ifBinaryWithConst(equality, (arg, constant) -> {\n            if (isNonEquality(other)) {\n                Expression left = other.getArguments().get(0);\n                Expression right = other.getArguments().get(1);\n                Expression arg2 = null;\n                Object constant2 = null;\n                if(Equi.equiExpressions(arg, left)) {\n                    constant2 = Nodes.getConstant(right);\n                    arg2 = left;\n                } else if(Equi.equiExpressions(arg, right)) {\n                    constant2 = Nodes.getConstant(left);\n                    arg2 = right;\n                }\n                if(constant2 != null && !Objects.equals(constant, constant2)) {\n                    // non-short-circuit logic is intended\n                    if (reported.add(arg) & reported.add(arg2)) {\n                        mc.report(\"OrNotEqualsAlwaysTrue\", 0, arg, CONST1.createFromConst(constant), CONST2\n                                .createFromConst(constant2));\n                    }\n                }\n            }\n            if (other.getCode() == AstCode.LogicalOr) {\n                checkNonEqual(equality, other.getArguments().get(0), mc);\n                checkNonEqual(equality, other.getArguments().get(1), mc);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/ExposeRepresentation.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.db.Mutability;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"MaliciousCode\", name = \"ExposeMutableFieldViaParameter\", maxScore = 35)\n@WarningDefinition(category = \"MaliciousCode\", name = \"ExposeMutableStaticFieldViaParameter\", maxScore = 50)\npublic class ExposeRepresentation {\n    @ClassVisitor\n    public boolean checkClass(TypeDefinition td) {\n        return td.isPublic();\n    }\n\n    @MethodVisitor\n    public boolean checkMethod(MethodDefinition md) {\n        return (md.isPublic() || md.isProtected()) && !md.getParameters().isEmpty();\n    }\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc, MethodDefinition md, Mutability m) {\n        FieldDefinition fd = getField(expr, md);\n        if (fd == null)\n            return;\n        Expression value = Exprs.getChild(expr, expr.getArguments().size() - 1);\n        ParameterDefinition pd = getParameter(value);\n        if (pd == null)\n            return;\n        if (!Types.isMutable(fd.getFieldType()) && !m.isKnownMutable(fd.getFieldType()))\n            return;\n        int priority = 0;\n        if (md.isProtected() || fd.isProtected())\n            priority += 10;\n        if (md.isVarArgs() && pd.getPosition() == md.getParameters().size() - 1)\n            priority += 10;\n        if (nc.getParent() == null && nc.getRoot().getBody().size() == 1)\n            priority += 15;\n        else if (!fd.isFinal())\n            priority += 3;\n        String type = fd.isStatic() ? \"ExposeMutableStaticFieldViaParameter\" : \"ExposeMutableFieldViaParameter\";\n        mc.report(type, priority, expr, Roles.FIELD_TYPE.create(fd.getFieldType()));\n    }\n\n    private FieldDefinition getField(Expression expr, MethodDefinition md) {\n        if (!md.isStatic() && expr.getCode() == AstCode.PutField) {\n            FieldDefinition fd = ((FieldReference) expr.getOperand()).resolve();\n            if (fd != null && !fd.isSynthetic() && (fd.isPrivate() || fd.isPackagePrivate() || fd.isProtected())) {\n                if (md.isProtected() && fd.isProtected())\n                    return null;\n                Expression self = Exprs.getChild(expr, 0);\n                if (!Exprs.isThis(self))\n                    return null;\n                return fd;\n            }\n        }\n        if (expr.getCode() == AstCode.PutStatic) {\n            FieldDefinition fd = ((FieldReference) expr.getOperand()).resolve();\n            if (fd != null && !fd.isSynthetic() && (fd.isPrivate() || fd.isPackagePrivate())) {\n                return fd;\n            }\n        }\n        return null;\n    }\n\n    private ParameterDefinition getParameter(Expression value) {\n        if (value.getOperand() instanceof ParameterDefinition)\n            return (ParameterDefinition) value.getOperand();\n        if (value.getOperand() instanceof Variable)\n            return ((Variable) value.getOperand()).getOriginalParameter();\n        return null;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/FieldAccess.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.core.ArrayUtilities;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.db.FieldStats;\nimport one.util.huntbugs.db.Mutability;\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.FieldContext;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.FieldVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.VisitOrder;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.AccessLevel;\nimport one.util.huntbugs.util.Annotations;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningAnnotation.Location;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"RedundantCode\", name=\"UnusedPrivateField\", maxScore=45)\n@WarningDefinition(category=\"RedundantCode\", name=\"UnusedPublicField\", maxScore=38)\n@WarningDefinition(category=\"RedundantCode\", name=\"UnreadPrivateField\", maxScore=48)\n@WarningDefinition(category=\"RedundantCode\", name=\"UnreadPublicField\", maxScore=37)\n@WarningDefinition(category=\"Correctness\", name=\"UnwrittenPrivateField\", maxScore=60)\n@WarningDefinition(category=\"Correctness\", name=\"UnwrittenPublicField\", maxScore=45)\n@WarningDefinition(category=\"Correctness\", name=\"FieldIsAlwaysNull\", maxScore=55)\n@WarningDefinition(category=\"Performance\", name=\"FieldShouldBeStatic\", maxScore=50)\n@WarningDefinition(category=\"Performance\", name=\"FieldUsedInSingleMethod\", maxScore=55)\n@WarningDefinition(category=\"MaliciousCode\", name=\"StaticFieldShouldBeFinal\", maxScore=55)\n@WarningDefinition(category=\"MaliciousCode\", name=\"StaticFieldShouldBeFinalAndPackagePrivate\", maxScore=55)\n@WarningDefinition(category=\"MaliciousCode\", name=\"StaticFieldCannotBeFinal\", maxScore=35)\n@WarningDefinition(category=\"MaliciousCode\", name=\"StaticFieldMutableArray\", maxScore=40)\n@WarningDefinition(category=\"MaliciousCode\", name=\"StaticFieldMutableCollection\", maxScore=45)\n@WarningDefinition(category=\"MaliciousCode\", name=\"StaticFieldMutable\", maxScore=40)\n@WarningDefinition(category=\"MaliciousCode\", name=\"StaticFieldShouldBeRefactoredToFinal\", maxScore=40)\n@WarningDefinition(category=\"MaliciousCode\", name=\"StaticFieldShouldBePackagePrivate\", maxScore=55)\n@WarningDefinition(category=\"MaliciousCode\", name=\"StaticFieldShouldBeNonInterfacePackagePrivate\", maxScore=30)\n@WarningDefinition(category = \"MaliciousCode\", name = \"ExposeMutableFieldViaReturnValue\", maxScore = 35)\n@WarningDefinition(category = \"MaliciousCode\", name = \"ExposeMutableStaticFieldViaReturnValue\", maxScore = 50)\n@WarningDefinition(category = \"MaliciousCode\", name = \"MutableEnumField\", maxScore = 55)\npublic class FieldAccess {\n    private static final Set<String> MUTABLE_COLLECTION_CLASSES = new HashSet<>(Arrays.asList(\"java/util/ArrayList\",\n        \"java/util/HashSet\", \"java/util/HashMap\", \"java/util/Hashtable\", \"java/util/IdentityHashMap\",\n        \"java/util/LinkedHashSet\", \"java/util/LinkedList\", \"java/util/LinkedHashMap\", \"java/util/TreeSet\",\n        \"java/util/TreeMap\", \"java/util/Properties\"));\n    \n    static class MethodLocation {\n        MethodDefinition md;\n        Location loc;\n\n        public MethodLocation(MethodDefinition md, Location loc) {\n            this.md = md;\n            this.loc = loc;\n        }\n        \n        public WarningAnnotation<?>[] getAnnotations() {\n            WarningAnnotation<?>[] anno = {Roles.METHOD.create(md), Roles.LOCATION.create(loc)};\n            return anno;\n        }\n    }\n    \n    static class FieldRecord {\n        MethodLocation firstWrite, firstRead, expose;\n        Object constant;\n        int numWrites;\n        boolean mutable;\n        boolean array;\n        boolean collection;\n        boolean usedInSingleMethod = true;\n        boolean hasSimpleSetter;\n    }\n    \n    private final Map<String, FieldRecord> fields = new HashMap<>();\n    private boolean fullyAnalyzed = true;\n\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visitCode(Expression expr, NodeChain nc, MethodContext mc, MethodDefinition md, TypeDefinition td, Mutability m) {\n        if(expr.getCode() == AstCode.PutField || expr.getCode() == AstCode.PutStatic ||\n                expr.getCode() == AstCode.GetField || expr.getCode() == AstCode.GetStatic) {\n            FieldDefinition fd = ((FieldReference) expr.getOperand()).resolve();\n            if(fd != null && !fd.isSynthetic() && fd.getDeclaringType().isEquivalentTo(td)) {\n                FieldRecord fieldRecord = fields.computeIfAbsent(fd.getName(), n -> new FieldRecord());\n                if(Nodes.isFieldRead(expr)) {\n                    if(fieldRecord.firstRead == null) {\n                        fieldRecord.firstRead = new MethodLocation(md, mc.getLocation(expr));\n                    }\n                    if (Inf.BACKLINK.findTransitiveUsages(expr, true).anyMatch(\n                        e -> e.getCode() == AstCode.Return\n                            && (e.getArguments().get(0).getCode() == AstCode.GetField || !ValuesFlow.hasUpdatedSource(e\n                                    .getArguments().get(0))))) {\n                        fieldRecord.expose = new MethodLocation(md, mc.getLocation(expr));\n                    }\n                } else {\n                    Expression value = Exprs.getChild(expr, expr.getArguments().size()-1);\n                    if(fieldRecord.firstWrite == null) {\n                        fieldRecord.firstWrite = new MethodLocation(md, mc.getLocation(expr));\n                        fieldRecord.constant = Nodes.getConstant(value);\n                    } else {\n                        if(fieldRecord.constant != null) {\n                            Object constant = Nodes.getConstant(value);\n                            if(!Objects.equals(fieldRecord.constant, constant))\n                                fieldRecord.constant = null;\n                        }\n                    }\n                    if (md.isPublic() && nc.getParent() == null && nc.getRoot().getBody().size() == 1 && (expr\n                            .getCode() == AstCode.PutField ^ md.isStatic()) && value\n                                    .getOperand() instanceof ParameterDefinition) {\n                        fieldRecord.hasSimpleSetter = true;\n                    }\n                    if(value.getCode() == AstCode.InitObject) {\n                        String typeName = ((MethodReference) value.getOperand()).getDeclaringType().getInternalName();\n                        if(MUTABLE_COLLECTION_CLASSES.contains(typeName)) {\n                            fieldRecord.mutable = true;\n                            fieldRecord.collection = true;\n                        }\n                    } else if(value.getCode() == AstCode.InvokeStatic) {\n                        MethodReference mr = (MethodReference) value.getOperand();\n                        if (isMutableCollectionFactory(value, mr)) {\n                            fieldRecord.mutable = true;\n                            fieldRecord.collection = true;\n                        }\n                    }\n                    if(fd.getFieldType().isArray() || value.getInferredType() != null && value.getInferredType().isArray()) {\n                        fieldRecord.array = true;\n                        if (!isEmptyArray(value)) {\n                            fieldRecord.mutable = true;\n                        }\n                    }\n                    if(m.isKnownMutable(fd.getFieldType())) {\n                        fieldRecord.mutable = true;\n                    }\n                    fieldRecord.numWrites++;\n                }\n                if(fieldRecord.usedInSingleMethod) {\n                    if (md.isTypeInitializer()) {\n                        fieldRecord.usedInSingleMethod = false;\n                    }\n                    if (Nodes.isFieldRead(expr) && ValuesFlow.getSource(expr) == expr) {\n                        fieldRecord.usedInSingleMethod = false;\n                    }\n                    if((expr.getCode() == AstCode.PutField || expr.getCode() == AstCode.GetField) && \n                            (md.isStatic() || !Exprs.isThis(Exprs.getChild(expr, 0)))) {\n                        fieldRecord.usedInSingleMethod = false;\n                    }\n                    if(fieldRecord.firstWrite != null && fieldRecord.firstWrite.md != md || \n                            fieldRecord.firstRead != null && fieldRecord.firstRead.md != md) {\n                        fieldRecord.usedInSingleMethod = false;\n                    }\n                }\n            }\n        }\n    }\n\n    private boolean isMutableCollectionFactory(Expression value, MethodReference mr) {\n        if (mr.getName().equals(\"asList\") && mr.getDeclaringType().getInternalName().equals(\"java/util/Arrays\")\n            && value.getArguments().size() == 1 && !isEmptyArray(Exprs.getChild(value, 0)))\n            return true;\n        if ((mr.getName().equals(\"newArrayList\") || mr.getName().equals(\"newLinkedList\"))\n            && mr.getDeclaringType().getInternalName().equals(\"com/google/common/collect/Lists\"))\n            return true;\n        if ((mr.getName().equals(\"newHashSet\") || mr.getName().equals(\"newTreeSet\"))\n            && mr.getDeclaringType().getInternalName().equals(\"com/google/common/collect/Sets\"))\n            return true;\n        return false;\n    }\n\n    private static boolean isEmptyArray(Expression value) {\n        return value.getCode() == AstCode.NewArray\n            && Integer.valueOf(0).equals(Nodes.getConstant(value.getArguments().get(0)));\n    }\n    \n    @MethodVisitor(order=VisitOrder.AFTER)\n    public void checkAnalyzed(MethodContext mc) {\n        fullyAnalyzed &= mc.isFullyAnalyzed();\n    }\n    \n    @FieldVisitor\n    public void visit(FieldContext fc, FieldDefinition fd, TypeDefinition td, FieldStats fs) {\n        if(fd.isSynthetic() || fd.isEnumConstant())\n            return;\n        int flags = fs.getFlags(fd);\n        if(Flags.testAny(flags, FieldStats.UNRESOLVED) || Annotations.hasAnnotation(fd, false)) {\n            return;\n        }\n        boolean isConstantType = fd.getFieldType().isPrimitive() || Types.isString(fd.getFieldType());\n        if(!Flags.testAny(flags, FieldStats.ACCESS)) {\n            if(fd.isStatic() && fd.isFinal() && isConstantType)\n                return;\n            // Autogenerated by javacc \n            if(fd.getName().equals(\"lengthOfMatch\") && td.getName().endsWith(\"TokenManager\"))\n                return;\n            fc.report(fd.isPublic() || fd.isProtected() ? \"UnusedPublicField\" : \"UnusedPrivateField\", fd.isPublic() ? 5 : 0);\n            return;\n        }\n        FieldRecord fieldRecord = fields.get(fd.getName());\n        if (fieldRecord != null && !fd.isStatic() && fd.isFinal() && fieldRecord.constant != null) {\n            fc.report(\"FieldShouldBeStatic\", 0, fieldRecord.firstWrite.getAnnotations());\n            return;\n        }\n        if(!Flags.testAny(flags, FieldStats.READ)) {\n            // Autogenerated by javacc \n            if(fd.getName().startsWith(\"jj\") && td.getName().endsWith(\"TokenManager\"))\n                return;\n            if(fd.getName().equals(\"errorCode\") && td.getSimpleName().equals(\"TokenMgrError\"))\n                return;\n            int priority = 0;\n            String warningType = fd.isPublic() || fd.isProtected() ? \"UnreadPublicField\" : \"UnreadPrivateField\";\n            if(fd.isPublic()) {\n                priority += 5;\n                if(fd.isFinal()) {\n                    priority += 5;\n                    if(fd.isStatic()) {\n                        priority += 10;\n                    }\n                }\n            }\n            if(!fd.isStatic() && !fd.isPublic() && fd.getName().startsWith(\"ref\") && fd.getFieldType().getSimpleType() == JvmType.Object) {\n                // probably field is used to keep strong reference\n                priority += 10;\n            }\n            fc.report(warningType, priority, getWriteAnnotations(fieldRecord));\n        }\n        if(checkWrite(fc, fd, td, fieldRecord, flags, isConstantType))\n            return;\n        if(checkNull(fc, fd, td, fieldRecord, flags))\n            return;\n        checkSingleMethod(fc, fd, fieldRecord, flags);\n        if(td.isEnum() && fieldRecord != null && !fd.isStatic()) {\n            boolean mutable = fieldRecord.mutable;\n            if(fd.isPublic() && (!fd.isFinal() || mutable)) {\n                fc.report(\"MutableEnumField\", 0, getWriteAnnotations(fieldRecord));\n                return;\n            }\n        }\n        if(fd.isStatic() && (fd.isPublic() || fd.isProtected()) && (td.isPublic() || td.isProtected())) {\n            boolean mutable = fieldRecord != null && fieldRecord.mutable;\n            if(!fd.isFinal() && Flags.testAny(flags, FieldStats.WRITE_CONSTRUCTOR) &&\n                    !Flags.testAny(flags, FieldStats.WRITE_CLASS | FieldStats.WRITE_PACKAGE | FieldStats.WRITE_OUTSIDE)) {\n                String type = \"StaticFieldShouldBeRefactoredToFinal\";\n                if(fieldRecord != null && fieldRecord.numWrites == 1) {\n                    type = \"StaticFieldShouldBeFinal\";\n                    if(mutable && !Flags.testAny(flags, FieldStats.READ_OUTSIDE)) {\n                        type = \"StaticFieldShouldBeFinalAndPackagePrivate\";\n                    }\n                }\n                fc.report(type, AccessLevel.of(fd).select(0, 10, 100, 100), getWriteAnnotations(fieldRecord));\n                return;\n            }\n            if(mutable || !fd.isFinal()) {\n                String type = null;\n                WarningAnnotation<?>[] anno = ArrayUtilities.append(getWriteAnnotations(fieldRecord),\n                    Roles.FIELD_TYPE.create(fd.getFieldType()));\n                if(!Flags.testAny(flags, FieldStats.WRITE_OUTSIDE | FieldStats.READ_OUTSIDE)) {\n                    type = td.isInterface() ? \"StaticFieldShouldBeNonInterfacePackagePrivate\"\n                        : \"StaticFieldShouldBePackagePrivate\";\n                } else if(!fd.isFinal()) {\n                    type = \"StaticFieldCannotBeFinal\";\n                } else if(mutable && fieldRecord.array) {\n                    type = \"StaticFieldMutableArray\";\n                } else if(mutable && fieldRecord.collection) {\n                    type = \"StaticFieldMutableCollection\";\n                } else if(mutable) {\n                    type = \"StaticFieldMutable\";\n                }\n                if(type != null) {\n                    fc.report(type, AccessLevel.of(fd).select(0, 10, 100, 100), anno);\n                    return;\n                }\n            }\n        }\n        if(fieldRecord != null && (td.isPublic() || td.isProtected()) && (fd.isPrivate() || fd.isPackagePrivate())) {\n            MethodLocation expose = fieldRecord.expose;\n            if(fieldRecord.mutable && expose != null && (expose.md.isPublic() || expose.md.isProtected())) {\n                String type = fd.isStatic() ? \"ExposeMutableStaticFieldViaReturnValue\" : \"ExposeMutableFieldViaReturnValue\";\n                int priority = AccessLevel.of(expose.md).select(0, 10, 100, 100);\n                if(fieldRecord.hasSimpleSetter)\n                    priority += 15;\n                else if(!fd.isFinal())\n                    priority += 3;\n                fc.report(type, priority, ArrayUtilities.append(expose\n                        .getAnnotations(), Roles.FIELD_TYPE.create(fd.getFieldType())));\n            }\n        }\n    }\n\n    private boolean checkWrite(FieldContext fc, FieldDefinition fd, TypeDefinition td, FieldRecord fieldRecord, int flags, boolean isConstantType) {\n        if(!Flags.testAny(flags, FieldStats.WRITE)) {\n            if(fd.isStatic() && fd.isFinal() && isConstantType)\n                return false;\n            WarningAnnotation<?>[] anno = {};\n            int priority = 0;\n            String warningType = fd.isPublic() || fd.isProtected() ? \"UnwrittenPublicField\" : \"UnwrittenPrivateField\";\n            if (fieldRecord != null && fieldRecord.firstRead != null) {\n                anno = fieldRecord.firstRead.getAnnotations();\n            }\n            if(fd.isPublic()) {\n                priority += 5;\n            }\n            priority += tweakForSerialization(fd, td);\n            if(fd.getFieldType().getSimpleType() == JvmType.Boolean) {\n                priority += 5;\n            }\n            if(fd.getName().equalsIgnoreCase(\"debug\")) {\n                priority += 5;\n            }\n            fc.report(warningType, priority, anno);\n            return true;\n        }\n        return false;\n    }\n\n    private int tweakForSerialization(FieldDefinition fd, TypeDefinition td) {\n        // Probably field is kept for backwards serialization compatibility\n        if(!fd.isStatic() && Types.isInstance(td, \"java/io/Serializable\")) {\n            return 10;\n        }\n        if(Flags.testAny(fd.getFlags(), Flags.TRANSIENT)) {\n            return 30;\n        }\n        return 0;\n    }\n\n    private boolean checkNull(FieldContext fc, FieldDefinition fd, TypeDefinition td, FieldRecord fieldRecord, int flags) {\n        if(!Flags.testAny(flags, FieldStats.WRITE_NONNULL) && Flags.testAny(flags, FieldStats.READ)) {\n            int priority = 0;\n            if(fd.isFinal() && fd.isStatic()) {\n                priority += 20;\n                String lcName = fd.getName().toLowerCase(Locale.ENGLISH);\n                if (lcName.contains(\"null\") || lcName.contains(\"zero\") || lcName.contains(\"empty\")) {\n                    priority += 15;\n                }\n            } else if(fd.isPublic()) {\n                priority += 10;\n            }\n            priority += tweakForSerialization(fd, td);\n            fc.report(\"FieldIsAlwaysNull\", priority, getWriteAnnotations(fieldRecord));\n            return true;\n        }\n        return false;\n    }\n\n    private void checkSingleMethod(FieldContext fc, FieldDefinition fd, FieldRecord fieldRecord, int flags) {\n        if (fullyAnalyzed\n            && Flags.testAny(flags, FieldStats.READ)\n            && !Flags.testAny(flags, FieldStats.READ_PACKAGE | FieldStats.READ_OUTSIDE | FieldStats.WRITE_PACKAGE\n                | FieldStats.WRITE_OUTSIDE) && fieldRecord != null && fieldRecord.usedInSingleMethod\n            && fieldRecord.firstWrite != null) {\n            // javacc-generated\n            if(fd.getName().startsWith(\"jj_\") && fd.getDeclaringType().getSimpleName().endsWith(\"Parser\") &&\n                    fieldRecord.firstWrite.md.getName().equals(\"generateParseException\"))\n                return;\n            int priority = AccessLevel.of(fd).select(10, 3, 1, 0);\n            if(!fd.isStatic())\n                priority += 5;\n            if(fieldRecord.firstWrite.md.isConstructor())\n                priority += 5;\n            if(fd.getFieldType().isPrimitive())\n                priority += 3;\n            fc.report(\"FieldUsedInSingleMethod\", priority, fieldRecord.firstWrite.getAnnotations());\n        }\n    }\n\n    private WarningAnnotation<?>[] getWriteAnnotations(FieldRecord fieldRecord) {\n        if (fieldRecord != null && fieldRecord.firstWrite != null) {\n            return fieldRecord.firstWrite.getAnnotations();\n        }\n        WarningAnnotation<?>[] anno = {};\n        return anno;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/FinalizerContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"BadPractice\", name = \"FinalizeNullifiesSuper\", maxScore = 50)\n@WarningDefinition(category = \"BadPractice\", name = \"FinalizeNoSuperCall\", maxScore = 45)\n@WarningDefinition(category = \"RedundantCode\", name = \"FinalizeEmpty\", maxScore = 35)\n@WarningDefinition(category = \"RedundantCode\", name = \"FinalizeUselessSuper\", maxScore = 40)\n@WarningDefinition(category = \"BadPractice\", name = \"FinalizeInvocation\", maxScore = 60)\n@WarningDefinition(category = \"BadPractice\", name = \"FinalizeNullsFields\", maxScore = 50)\n@WarningDefinition(category = \"BadPractice\", name = \"FinalizeOnlyNullsFields\", maxScore = 65)\n@WarningDefinition(category = \"MaliciousCode\", name = \"FinalizePublic\", maxScore = 60)\npublic class FinalizerContract {\n    @AstVisitor(nodes = AstNodes.ROOT, methodName = \"finalize\", methodSignature = \"()V\")\n    public void visitFinalizer(Block body, MethodContext mc, MethodDefinition md) {\n        MethodDefinition superfinalizer = getSuperfinalizer(md.getDeclaringType());\n        if (md.isPublic()) {\n            mc.report(\"FinalizePublic\", 0, body);\n        }\n        if (superfinalizer != null) {\n            if (body.getBody().isEmpty())\n                mc.report(\"FinalizeNullifiesSuper\", 0, body, Roles.SUPERCLASS.create(superfinalizer\n                        .getDeclaringType()));\n            else {\n                if (body.getBody().size() == 1) {\n                    Node child = body.getBody().get(0);\n                    if (isSuperCall(child)) {\n                        if (!md.isFinal()) {\n                            mc.report(\"FinalizeUselessSuper\", 0, child, Roles.SUPERCLASS.create(superfinalizer\n                                    .getDeclaringType()));\n                            return;\n                        }\n                    }\n                }\n                if (Nodes.find(body, this::isSuperCall) == null) {\n                    mc.report(\"FinalizeNoSuperCall\", 0, Roles.SUPERCLASS.create(superfinalizer.getDeclaringType()));\n                }\n            }\n        } else {\n            if (body.getBody().isEmpty() && !md.isFinal()) {\n                mc.report(\"FinalizeEmpty\", 0, body);\n            }\n        }\n        boolean hasNullField = false, hasSomethingElse = false;\n        for (Node node : body.getBody()) {\n            if (Nodes.isOp(node, AstCode.PutField) && Nodes.isOp(Nodes.getChild(node, 1), AstCode.AConstNull))\n                hasNullField = true;\n            else\n                hasSomethingElse = true;\n        }\n        if (hasNullField) {\n            mc.report(hasSomethingElse ? \"FinalizeNullsFields\" : \"FinalizeOnlyNullsFields\", 0, body);\n        }\n    }\n\n    private boolean isSuperCall(Node child) {\n        return Nodes.isOp(child, AstCode.InvokeSpecial) && isFinalizer((MethodReference) (((Expression) child)\n                .getOperand()));\n    }\n\n    @AstVisitor\n    public void visit(Node node, MethodContext mc, MethodDefinition md) {\n        if (Nodes.isOp(node, AstCode.InvokeVirtual) && isFinalizer((MethodReference) ((Expression) node)\n                .getOperand())) {\n            mc.report(\"FinalizeInvocation\", isFinalizer(md) ? 10 : 0, node);\n        }\n    }\n\n    private static boolean isFinalizer(MethodReference mr) {\n        return mr.getName().equals(\"finalize\") && mr.getSignature().equals(\"()V\");\n    }\n\n    private static MethodDefinition getSuperfinalizer(TypeDefinition type) {\n        TypeDefinition superType = type.getBaseType().resolve();\n        if (superType == null || Types.isObject(superType))\n            return null;\n        for (MethodDefinition child : superType.getDeclaredMethods()) {\n            if (isFinalizer(child))\n                return child;\n        }\n        return getSuperfinalizer(superType);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/FloatingPointComparison.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Locale;\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"BadPractice\", name = \"FloatComparison\", maxScore = 40)\npublic class FloatingPointComparison {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression node, MethodContext ctx, MethodDefinition md) {\n        if (node.getCode() != AstCode.CmpEq && node.getCode() != AstCode.CmpNe)\n            return;\n        List<Expression> args = node.getArguments();\n        TypeReference inferredType = args.get(0).getInferredType();\n        if(inferredType == null)\n            return;\n        JvmType type = inferredType.getSimpleType();\n        if (type != JvmType.Double && type != JvmType.Float)\n            return;\n        Expression leftArg = Exprs.getChild(node, 0);\n        Expression rightArg = Exprs.getChild(node, 1);\n        Object left = Nodes.getConstant(leftArg);\n        Object right = Nodes.getConstant(rightArg);\n        int priority = tweakPriority(args.get(0)) + tweakPriority(args.get(1));\n        if(ValuesFlow.anyMatch(leftArg, rightArg::equals) || ValuesFlow.anyMatch(rightArg, leftArg::equals))\n            priority += 20;\n        String lcName = md.getName().toLowerCase(Locale.ENGLISH);\n        if (lcName.contains(\"equal\") || lcName.contains(\"compare\"))\n            priority += 20;\n        Number n = left instanceof Number ? (Number) left : right instanceof Number ? (Number) right : null;\n        if(n != null)\n            ctx.report(\"FloatComparison\", priority, node, Roles.NUMBER.create(n));\n        else\n            ctx.report(\"FloatComparison\", priority, node);\n    }\n\n    private int tweakPriority(Expression expr) {\n        Object val = Nodes.getConstant(expr);\n        if (val instanceof Double || val instanceof Float) {\n            double v = ((Number) val).doubleValue();\n            float f = ((Number) val).floatValue();\n            if (v == 0.0 || Double.isInfinite(v) || Double.isNaN(v))\n                return 50;\n            if (v == 1.0 || v == 2.0 || v == -1.0 || v == -2.0 || v == Double.MIN_VALUE || v == Double.MAX_VALUE\n                || v == -Double.MAX_VALUE || v == -Double.MIN_VALUE || f == Float.MAX_VALUE || f == Float.MIN_VALUE ||\n                f == -Float.MAX_VALUE || f == -Float.MIN_VALUE)\n                return 40;\n            if (v == 3.0 || v == 4.0 || v == -3.0 || v == -4.0)\n                return 30;\n            int prec = new BigDecimal(v).precision();\n            if (prec < 3)\n                return 25;\n            if (prec < 7)\n                return 20;\n            if (prec < 10)\n                return 15;\n            return 5;\n        }\n        if (expr.getCode() == AstCode.InvokeStatic) {\n            MethodReference method = (MethodReference) expr.getOperand();\n            if(method.getName().equals(\"floor\") || method.getName().equals(\"round\") || method.getName().equals(\"rint\")) {\n                return method.getDeclaringType().getInternalName().equals(\"java/lang/Math\") ? 50 : 35;\n            }\n        }\n        if (Nodes.isToFloatingPointConversion(expr)) {\n            Expression arg = expr.getArguments().get(0);\n            // check like if ((int)d == d)\n            if(arg.getCode() == AstCode.D2I || arg.getCode() == AstCode.D2L || arg.getCode() == AstCode.F2I || arg.getCode() == AstCode.F2L)\n                return 40;\n            return 20;\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/FloatingPointNaN.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.decompiler.ast.Expression;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Role.StringRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"FloatCompareToNaN\", maxScore = 90)\npublic class FloatingPointNaN {\n    private static final StringRole USED_TYPE = StringRole.forName(\"USED_TYPE\");\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression node, MethodContext ctx) {\n        if(node.getCode().isComparison()) {\n            Nodes.ifBinaryWithConst(node, (arg, constant) -> {\n                if(constant instanceof Float && Float.isNaN((float) constant)) {\n                    ctx.report(\"FloatCompareToNaN\", 0, arg, \n                        Roles.REPLACEMENT_METHOD.create(\"java/lang/Float\", \"isNaN\", \"(F)Z\"),\n                        USED_TYPE.create(\"float\"));\n                } else if(constant instanceof Double && Double.isNaN((double) constant)) {\n                    ctx.report(\"FloatCompareToNaN\", 0, arg, Roles.REPLACEMENT_METHOD.create(\"java/lang/Double\", \"isNaN\", \"(D)Z\"),\n                        USED_TYPE.create(\"double\"));\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/IgnoredException.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.CatchBlock;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"BadPractice\", name=\"IgnoredException\", maxScore=43)\npublic class IgnoredException {\n    @AstVisitor\n    public void visit(Node node, MethodContext mc) {\n        if(node instanceof CatchBlock) {\n            CatchBlock cb = (CatchBlock)node;\n            TypeReference tr = cb.getExceptionType();\n            if(tr != null && isTrivial(cb.getBody(), cb.getExceptionVariable())) {\n                int priority = getExceptionPriority(tr);\n                if(priority >= 0) {\n                    mc.report(\"IgnoredException\", priority, node, Roles.EXCEPTION.create(tr));\n                }\n            }\n        }\n    }\n    \n    private static int getExceptionPriority(TypeReference tr) {\n        switch(tr.getInternalName()) {\n        case \"java/lang/Throwable\":\n            return 0;\n        case \"java/lang/Error\":\n            return 1;\n        case \"java/lang/Exception\":\n            return 5;\n        case \"java/lang/RuntimeException\":\n            return 7;\n        default:\n            return -1;\n        }\n    }\n\n    private boolean isTrivial(List<Node> body, Variable variable) {\n        if(body.isEmpty())\n            return true;\n        if(body.size() == 1) {\n            Node node = body.get(0);\n            if(Nodes.isOp(node, AstCode.LoopContinue) || Nodes.isOp(node, AstCode.LoopOrSwitchBreak))\n                return true;\n            if(Nodes.isOp(node, AstCode.Return)) {\n                return Exprs.stream((Expression) node).noneMatch(e -> e.getOperand() == variable);\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/IncorrectVarArg.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"PrimitiveArrayPassedAsVarArg\", maxScore=60)\npublic class IncorrectVarArg {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if(expr.getOperand() instanceof MethodReference) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if(mr.getErasedSignature().contains(\"[Ljava/lang/Object;)\")) {\n                Expression lastArg = expr.getArguments().get(expr.getArguments().size()-1);\n                if(lastArg.getCode() == AstCode.InitArray && lastArg.getArguments().size() == 1) {\n                    TypeReference nested = lastArg.getArguments().get(0).getInferredType();\n                    if(nested != null && nested.isArray() && nested.getElementType().isPrimitive()) {\n                        int priority = 0;\n                        if(!highPriority(mr)) {\n                            priority = 10;\n                            MethodDefinition md = mr.resolve();\n                            if(md == null) {\n                                priority = 20;\n                            } else if(!md.isVarArgs())\n                                return;\n                        }\n                        mc.report(\"PrimitiveArrayPassedAsVarArg\", priority, lastArg, Roles.CALLED_METHOD.create(mr),\n                            Roles.ARRAY_TYPE.create(nested));\n                    }\n                }\n            }\n        }\n    }\n\n    private static boolean highPriority(MethodReference mr) {\n        return mr.getName().equals(\"asList\") && mr.getDeclaringType().getInternalName().equals(\"java/util/Arrays\") ||\n                mr.getName().equals(\"of\") && mr.getDeclaringType().getInternalName().equals(\"java/util/stream/Stream\");\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/InfiniteLoop.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Loop;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"InfiniteLoop\", maxScore = 90)\n@WarningDefinition(category = \"Correctness\", name = \"InvariantLoopCondition\", maxScore = 60)\n@WarningDefinition(category = \"Correctness\", name = \"InvariantLoopConditionPart\", maxScore = 55)\npublic class InfiniteLoop {\n    @AstVisitor\n    public void visit(Node node, MethodContext mc) {\n        if (node instanceof Loop) {\n            Loop loop = (Loop) node;\n            if (loop.getCondition() == null)\n                return;\n            Expression expr = loop.getCondition();\n            if(!Nodes.isSideEffectFree(expr))\n                return;\n            checkCondition(mc, loop, expr, null);\n        }\n    }\n\n    private void checkCondition(MethodContext mc, Loop loop, Expression expr, AstCode parent) {\n        if((expr.getCode() == AstCode.LogicalAnd || expr.getCode() == AstCode.LogicalOr)\n                && (parent == null || expr.getCode() == parent)) {\n            // suppress warning for quite common scenario like \"for(int x = 0; data != null && x < data.length; x++)\"\n            if(!Nodes.isNullCheck(expr.getArguments().get(0))) {\n                checkCondition(mc, loop, expr.getArguments().get(0), expr.getCode());\n            }\n            checkCondition(mc, loop, expr.getArguments().get(1), expr.getCode());\n            return;\n        }\n        if (expr.getCode() == AstCode.LogicalNot) {\n            checkCondition(mc, loop, expr.getArguments().get(0), parent == AstCode.LogicalAnd ? AstCode.LogicalOr\n                    : parent == AstCode.LogicalOr ? AstCode.LogicalAnd : parent);\n            return;\n        }\n        // Will be reported as ResultOfComparisonIsStaticallyKnown\n        if (expr.getCode().isComparison() && Nodes.getConstant(expr) != null)\n            return;\n        if (!Nodes.isPure(expr))\n            return;\n        Set<Variable> vars = Exprs.stream(expr).filter(e -> e.getCode() == AstCode.Load).map(\n            e -> (Variable) e.getOperand()).collect(Collectors.toSet());\n        if(vars.isEmpty())\n            return;\n        class LoopState {\n            boolean hasControlFlow, hasLoads, hasStores;\n        }\n        LoopState ls = new LoopState();\n        loop.getBody().getChildrenAndSelfRecursive().forEach(n -> {\n            if(!(n instanceof Expression))\n                return;\n            Expression e = (Expression) n;\n            if (e.getCode() == AstCode.LoopOrSwitchBreak || e.getCode() == AstCode.Return\n                    || e.getCode() == AstCode.AThrow || e.getCode() == AstCode.Goto)\n                    ls.hasControlFlow = true;\n            if (e.getOperand() instanceof Variable && vars.contains(e.getOperand())) {\n                ls.hasLoads = true;\n                if(e.getCode() == AstCode.Store || e.getCode() == AstCode.Inc)\n                    ls.hasStores = true;\n            }\n            if (e.getCode() == AstCode.PreIncrement || e.getCode() == AstCode.PostIncrement) {\n                if(vars.contains(e.getArguments().get(0).getOperand()))\n                    ls.hasStores = true;\n            }\n        });\n        if(parent == null) {\n            if(!ls.hasControlFlow && !ls.hasStores) {\n                mc.report(\"InfiniteLoop\", 0, loop, Roles.VARIABLE.create(vars.iterator().next().getName()),\n                    Roles.EXPRESSION.create(expr));\n            } else if(!ls.hasLoads) {\n                mc.report(\"InvariantLoopCondition\", 0, expr, Roles.VARIABLE.create(vars.iterator().next().getName()),\n                    Roles.EXPRESSION.create(expr));\n            }\n        } else if((!ls.hasControlFlow && !ls.hasStores) || !ls.hasLoads) {\n            mc.report(\"InvariantLoopConditionPart\", 0, expr, Roles.VARIABLE.create(vars.iterator().next().getName()),\n                Roles.EXPRESSION.create(expr));\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/InfiniteRecursion.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Lambda;\nimport com.strobel.decompiler.ast.Loop;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Switch;\nimport com.strobel.decompiler.ast.TryCatchBlock;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"InfiniteRecursion\", maxScore = 90)\npublic class InfiniteRecursion {\n    boolean stateChange;\n    boolean controlTransfer;\n\n    @MethodVisitor\n    public void init() {\n        stateChange = controlTransfer = false;\n    }\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public boolean visit(Expression expr, NodeChain nc, MethodContext mc, MethodDefinition md) {\n        if (selfCall(expr, md) && (!stateChange && checkArgs(expr) || !controlTransfer && checkControlFlow(nc))) {\n            mc.report(\"InfiniteRecursion\", 0, expr);\n        }\n        if (expr.getCode() == AstCode.StoreElement || expr.getCode() == AstCode.PutField\n            || expr.getCode() == AstCode.PutStatic) {\n            stateChange = true;\n        }\n        if ((Nodes.isInvoke(expr) || expr.getCode() == AstCode.InitObject) && !Nodes.isSideEffectFree(expr)) {\n            stateChange = true;\n        }\n        if (expr.getCode() == AstCode.Return || expr.getCode() == AstCode.AThrow\n            || expr.getCode() == AstCode.LoopContinue || expr.getCode() == AstCode.LoopOrSwitchBreak\n            || expr.getCode() == AstCode.Goto) {\n            controlTransfer = true;\n        }\n        if (controlTransfer && stateChange)\n            return false;\n        return true;\n    }\n\n    private boolean selfCall(Expression expr, MethodDefinition md) {\n        if (!(expr.getOperand() instanceof MethodReference))\n            return false;\n        MethodReference mr = (MethodReference) expr.getOperand();\n        if (!mr.isEquivalentTo(md))\n            return false;\n        switch (expr.getCode()) {\n        case InvokeStatic:\n            return md.isStatic();\n        case InitObject:\n        case InvokeSpecial:\n            return md.isConstructor();\n        case InvokeVirtual:\n        case InvokeInterface:\n            return !md.isStatic() && Exprs.isThis(expr.getArguments().get(0));\n        default:\n            return false;\n        }\n    }\n\n    private boolean checkControlFlow(NodeChain nc) {\n        while (nc != null) {\n            Node node = nc.getNode();\n            if (node instanceof Condition || node instanceof Switch || node instanceof Loop\n                || node instanceof TryCatchBlock || node instanceof Lambda)\n                return false;\n            if (node instanceof Expression) {\n                Expression expr = (Expression)node;\n                if(expr.getCode() == AstCode.LogicalAnd || expr.getCode() == AstCode.LogicalOr || expr.getCode() == AstCode.TernaryOp)\n                    return false;\n            }\n            nc = nc.getParent();\n        }\n        return true;\n    }\n\n    private boolean checkArgs(Expression expr) {\n        List<Expression> args = expr.getArguments();\n        if ((expr.getCode() == AstCode.InvokeInterface || expr.getCode() == AstCode.InvokeVirtual)\n            && !Exprs.isThis(expr.getArguments().get(0)))\n            return false;\n        int base = (expr.getCode() == AstCode.InvokeStatic || expr.getCode() == AstCode.InitObject) ? 0 : 1;\n        for (int i = base; i < args.size(); i++) {\n            Expression arg = args.get(i);\n            Object operand = ValuesFlow.getSource(arg).getOperand();\n            if (!(operand instanceof ParameterDefinition))\n                return false;\n            ParameterDefinition pd = (ParameterDefinition) operand;\n            if (pd.getPosition() != i - base)\n                return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/InitializerRefersSubclass.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"Multithreading\", name=\"InitializerRefersSubclass\", maxScore=40)\npublic class InitializerRefersSubclass {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS, methodName=\"<clinit>\")\n    public void visit(Expression expr, NodeChain nc, MethodContext mc, TypeDefinition td) {\n        if(expr.getOperand() instanceof MemberReference) {\n            MemberReference mr = (MemberReference) expr.getOperand();\n            TypeReference tr = mr.getDeclaringType();\n            TypeDefinition subType = tr == null ? null : tr.resolve();\n            if (subType != null && (subType.isAnonymous() || subType.isLocalClass())) {\n                subType = subType.getBaseType().resolve();\n            }\n            if (subType != null && !td.isEquivalentTo(subType) && Types.isInstance(subType, td) && nc\n                    .getLambdaMethod() == null) {\n                mc.report(\"InitializerRefersSubclass\", td.isNonPublic() || subType.isNonPublic() ? 5 : 0, expr,\n                    Roles.SUBCLASS.create(subType));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/Internationalization.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Internationalization\", name=\"ConvertCaseWithDefaultLocale\", maxScore=25)\n@WarningDefinition(category=\"Internationalization\", name=\"MethodReliesOnDefaultEncoding\", maxScore=40)\npublic class Internationalization {\n    private static final Map<MemberInfo, MemberInfo> defEncodingMethods = new HashMap<>();\n    \n    private static void add(String className, String methodName, String badSignature, String goodSignature) {\n        defEncodingMethods.put(new MemberInfo(className, methodName, badSignature), \n            goodSignature == null ? null : new MemberInfo(className, methodName, goodSignature));\n    }\n    \n    static {\n        add(\"java/lang/String\", \"getBytes\", \"()[B\", \"(Ljava/nio/charset/Charset;)[B\");\n        add(\"java/lang/String\", \"<init>\", \"([B)V\", \"([BLjava/nio/charset/Charset;)V\");\n        add(\"java/lang/String\", \"<init>\", \"([BII)V\", \"([BIILjava/nio/charset/Charset;)V\");\n        add(\"java/io/ByteArrayOutputStream\", \"toString\", \"()Ljava/lang/String;\", \"(Ljava/lang/String;)Ljava/lang/String;\");\n        add(\"java/io/FileReader\", \"<init>\", \"(Ljava/lang/String;)V\", null);\n        add(\"java/io/FileReader\", \"<init>\", \"(Ljava/io/File;)V\", null);\n        add(\"java/io/FileReader\", \"<init>\", \"(Ljava/io/FileDescriptor;)V\", null);\n        add(\"java/io/FileWriter\", \"<init>\", \"(Ljava/lang/String;)V\", null);\n        add(\"java/io/FileWriter\", \"<init>\", \"(Ljava/lang/String;Z)V\", null);\n        add(\"java/io/FileWriter\", \"<init>\", \"(Ljava/io/File;)V\", null);\n        add(\"java/io/FileWriter\", \"<init>\", \"(Ljava/io/File;Z)V\", null);\n        add(\"java/io/FileWriter\", \"<init>\", \"(Ljava/io/FileDescriptor;)V\", null);\n        add(\"java/io/InputStreamReader\", \"<init>\", \"(Ljava/io/InputStream;)V\", \"(Ljava/io/InputStream;Ljava/nio/charset/Charset;)V\");\n        add(\"java/io/OutputStreamWriter\", \"<init>\", \"(Ljava/io/OutputStream;)V\", \"(Ljava/io/OutputStream;Ljava/nio/charset/Charset;)V\");\n        add(\"java/io/PrintStream\", \"<init>\", \"(Ljava/io/File;)V\", \"(Ljava/io/File;Ljava/lang/String;)V\");\n        add(\"java/io/PrintStream\", \"<init>\", \"(Ljava/io/OutputStream;)V\", \"(Ljava/io/OutputStream;ZLjava/lang/String;)V\");\n        add(\"java/io/PrintStream\", \"<init>\", \"(Ljava/io/OutputStream;Z)V\", \"(Ljava/io/OutputStream;ZLjava/lang/String;)V\");\n        add(\"java/io/PrintStream\", \"<init>\", \"(Ljava/lang/String;)V\", \"(Ljava/lang/String;Ljava/lang/String;)V\");\n        add(\"java/io/PrintWriter\", \"<init>\", \"(Ljava/io/File;)V\", \"(Ljava/io/File;Ljava/lang/String;)V\");\n        add(\"java/io/PrintWriter\", \"<init>\", \"(Ljava/io/OutputStream;)V\", null);\n        add(\"java/io/PrintWriter\", \"<init>\", \"(Ljava/io/OutputStream;Z)V\", null);\n        add(\"java/io/PrintWriter\", \"<init>\", \"(Ljava/lang/String;)V\", \"(Ljava/lang/String;Ljava/lang/String;)V\");\n        add(\"java/util/Scanner\", \"<init>\", \"(Ljava/io/File;)V\", \"(Ljava/io/File;Ljava/lang/String;)V\");\n        add(\"java/util/Scanner\", \"<init>\", \"(Ljava/nio/file/Path;)V\", \"(Ljava/nio/file/Path;Ljava/lang/String;)V\");\n        add(\"java/util/Scanner\", \"<init>\", \"(Ljava/io/InputStream;)V\", \"(Ljava/io/InputStream;Ljava/lang/String;)V\");\n        add(\"java/util/Scanner\", \"<init>\", \"(Ljava/nio/channels/ReadableByteChannel;)V\", \"(Ljava/nio/channels/ReadableByteChannel;Ljava/lang/String;)V\");\n        add(\"java/util/Formatter\", \"<init>\", \"(Ljava/lang/String;)V\", \"(Ljava/lang/String;Ljava/lang/String;)V\");\n        add(\"java/util/Formatter\", \"<init>\", \"(Ljava/io/File;)V\", \"(Ljava/io/File;Ljava/lang/String;)V\");\n        add(\"java/util/Formatter\", \"<init>\", \"(Ljava/io/OutputStream;)V\", \"(Ljava/io/OutputStream;Ljava/lang/String;)V\");\n    }\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if(expr.getCode() == AstCode.InvokeVirtual || expr.getCode() == AstCode.InitObject) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if(mr.getDeclaringType().getInternalName().equals(\"java/lang/String\") && mr.getSignature().equals(\"()Ljava/lang/String;\")\n                    && (mr.getName().equals(\"toUpperCase\") || mr.getName().equals(\"toLowerCase\"))) {\n                mc.report(\"ConvertCaseWithDefaultLocale\", 0, expr, Roles.REPLACEMENT_METHOD.create(mr.getDeclaringType()\n                        .getInternalName(), mr.getName(), \"(Ljava/util/Locale;)Ljava/lang/String;\"));\n            } else {\n                MemberInfo mi = new MemberInfo(mr);\n                if(defEncodingMethods.containsKey(mi)) {\n                    if(!expr.getArguments().isEmpty()) {\n                        Expression arg = Exprs.getChild(expr, 0);\n                        if(arg.getCode() == AstCode.GetStatic) {\n                            FieldReference fr = (FieldReference) arg.getOperand();\n                            if(fr.getDeclaringType().getInternalName().equals(\"java/lang/System\")) {\n                                // Usages like new Formatter(System.out) are considered ok\n                                return;\n                            }\n                        }\n                    }\n                    MemberInfo replacement = defEncodingMethods.get(mi);\n                    if(replacement != null) {\n                        mc.report(\"MethodReliesOnDefaultEncoding\", replacement.getSignature().contains(\"Charset\") ? 0\n                                : 3, expr, Roles.REPLACEMENT_METHOD.create(replacement));\n                    } else {\n                        mc.report(\"MethodReliesOnDefaultEncoding\", 10, expr);\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/InvalidMinMax.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.NumberRole;\nimport one.util.huntbugs.warning.Role.StringRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"InvalidMinMax\", maxScore = 80)\npublic class InvalidMinMax {\n    private static final NumberRole OUTER_NUMBER = NumberRole.forName(\"OUTER_NUMBER\");\n    private static final NumberRole INNER_NUMBER = NumberRole.forName(\"INNER_NUMBER\");\n    private static final StringRole OUTER_FUNC = StringRole.forName(\"OUTER_FUNC\");\n    private static final StringRole INNER_FUNC = StringRole.forName(\"INNER_FUNC\");\n\n    private static final int NONE = 0;\n\tprivate static final int MIN = -1;\n\tprivate static final int MAX = 1;\n\n\tprivate static int detectMethod(Node node) {\n\t\tif (!Nodes.isOp(node, AstCode.InvokeStatic)\n\t\t\t\t|| node.getChildren().size() != 2)\n\t\t\treturn NONE;\n\t\tMethodReference mr = (MethodReference) ((Expression) node).getOperand();\n\t\tif (!mr.getDeclaringType().getPackageName().equals(\"java.lang\"))\n\t\t\treturn NONE;\n\t\tif (mr.getName().equals(\"max\"))\n\t\t\treturn MAX;\n\t\tif (mr.getName().equals(\"min\"))\n\t\t\treturn MIN;\n\t\treturn NONE;\n\t}\n\n\t@AstVisitor\n\tpublic void visit(Node node, MethodContext mc) {\n\t\tint outer = detectMethod(node);\n\t\tif (outer == NONE)\n\t\t\treturn;\n\t\tNode left = Nodes.getChild(node, 0);\n\t\tNode right = Nodes.getChild(node, 1);\n\t\tint leftChild = detectMethod(left);\n\t\tint rightChild = detectMethod(right);\n\t\tif (leftChild == NONE && rightChild == NONE)\n\t\t\treturn;\n\t\tif (outer == leftChild || outer == rightChild\n\t\t\t\t|| leftChild == rightChild)\n\t\t\treturn;\n\t\tif (rightChild != NONE) {\n\t\t\tNode tmp = left;\n\t\t\tleft = right;\n\t\t\tright = tmp;\n\t\t}\n\t\tObject outerConst = Nodes.getConstant(right);\n\t\tif (!(outerConst instanceof Number))\n\t\t\treturn;\n\t\tNode expr = left.getChildren().get(0);\n\t\tObject innerConst = Nodes.getConstant(expr);\n\t\tif (!(innerConst instanceof Number)) {\n\t\t\tinnerConst = Nodes.getConstant(left.getChildren().get(1));\n\t\t} else {\n\t\t\texpr = left.getChildren().get(1);\n\t\t}\n\t\tif (!(innerConst instanceof Number))\n\t\t\treturn;\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tint cmp = ((Comparable<Object>) outerConst).compareTo(innerConst)\n\t\t\t\t* outer;\n\t\tif (cmp > 0)\n            mc.report(\"InvalidMinMax\", 0, expr, OUTER_NUMBER.create((Number) outerConst), OUTER_FUNC.create(outer == MAX\n                    ? \"max\" : \"min\"), INNER_NUMBER.create((Number) innerConst), INNER_FUNC.create(outer == MAX ? \"min\"\n                            : \"max\"));\n\t}\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/IteratorContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.NoSuchElementException;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"BadPractice\", name = \"IteratorHasNextCallsNext\", maxScore = 70)\n@WarningDefinition(category = \"BadPractice\", name = \"IteratorNoThrow\", maxScore = 60)\npublic class IteratorContract {\n    @MethodVisitor\n    public boolean check(TypeDefinition td) {\n        return Types.isInstance(td, \"java/util/Iterator\");\n    }\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS, methodName = \"hasNext\", methodSignature = \"()Z\")\n    public void visitHasNext(Expression expr, MethodContext mc, TypeDefinition td) {\n        if (expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if (mr.getName().equals(\"next\") && mr.getParameters().isEmpty() && Exprs.isThis(Exprs.getChild(expr, 0))) {\n                mc.report(\"IteratorHasNextCallsNext\", td.isPublic() ? 0 : 30, expr);\n            }\n        }\n    }\n\n    @AstVisitor(nodes = AstNodes.ROOT, methodName = \"next\")\n    public void visitNext(Block body, MethodContext mc, MethodDefinition md, TypeDefinition td) {\n        if (md.getErasedSignature().startsWith(\"()\")) {\n            AtomicBoolean sawCall = new AtomicBoolean();\n            Node found = Nodes.find(body, n -> {\n                if (Nodes.isOp(n, AstCode.AThrow)) {\n                    Expression exc = (Expression) Nodes.getChild(n, 0);\n                    if (Types.is(exc.getInferredType(), NoSuchElementException.class))\n                        return true;\n                }\n                if (n instanceof Expression) {\n                    Expression expr = (Expression) n;\n                    if (expr.getCode() == AstCode.InvokeSpecial || expr.getCode() == AstCode.InvokeInterface\n                        || expr.getCode() == AstCode.InvokeVirtual) {\n                        MethodReference mr = (MethodReference) expr.getOperand();\n                        if (Exprs.isThis(Exprs.getChild(expr, 0)) || mr.getName().contains(\"next\") || mr.getName().contains(\"previous\"))\n                            return true;\n                        if (!sawCall.get() && !Inf.PURITY.isSideEffectFree(expr)) {\n                            sawCall.set(true);\n                        }\n                    }\n                }\n                return false;\n            });\n            if (found != null)\n                return;\n            int priority = 0;\n            if(td.isNonPublic())\n                priority += 5;\n            if(sawCall.get())\n                priority += 30;\n            mc.report(\"IteratorNoThrow\", priority, body);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/JcipProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.annotations.CustomAnnotation;\n\nimport one.util.huntbugs.registry.FieldContext;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.FieldVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"BadPractice\", name=\"NonFinalFieldInImmutableClass\", maxScore=60)\npublic class JcipProblems {\n    @ClassVisitor\n    public boolean checkClass(TypeDefinition td) {\n        for(CustomAnnotation ca : td.getAnnotations()) {\n            String name = ca.getAnnotationType().getInternalName();\n            if(name.equals(\"net/jcip/annotations/Immutable\") || name.equals(\"javax/annotation/concurrent/Immutable\"))\n                return true;\n        }\n        return false;\n    }\n    \n    @FieldVisitor\n    public void visitField(FieldDefinition fd, FieldContext fc) {\n        if(!Flags.testAny(fd.getFlags(), Flags.VOLATILE | Flags.FINAL | Flags.TRANSIENT | Flags.STATIC)) {\n            fc.report(\"NonFinalFieldInImmutableClass\", 0);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/KnownComparison.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport one.util.huntbugs.flow.CFG.EdgeType;\nimport one.util.huntbugs.flow.CodeBlock;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.StringRole;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"RedundantCode\", name = \"ResultOfComparisonIsStaticallyKnown\", maxScore = 50)\n@WarningDefinition(category = \"RedundantCode\", name = \"ResultOfComparisonIsStaticallyKnownDeadCode\", maxScore = 70)\npublic class KnownComparison {\n    private static final StringRole RESULT = StringRole.forName(\"RESULT\");\n    private static final StringRole LEFT_OPERAND = StringRole.forName(\"LEFT_OPERAND\");\n    private static final StringRole RIGHT_OPERAND = StringRole.forName(\"RIGHT_OPERAND\");\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if (expr.getCode().isComparison() || (expr.getCode() == AstCode.InvokeVirtual && Methods.isEqualsMethod(\n            (MethodReference) expr.getOperand()))) {\n            Object result = Nodes.getConstant(expr);\n            if (result instanceof Boolean && !Exprs.isAssertion(expr)) {\n                Object left = Nodes.getConstant(expr.getArguments().get(0));\n                Object right = Nodes.getConstant(expr.getArguments().get(1));\n                if (left != null && right != null) {\n                    CodeBlock deadCode = mc.findDeadCode(expr, (boolean) result ? EdgeType.FALSE : EdgeType.TRUE);\n                    if (deadCode == null) {\n                        mc.report(\"ResultOfComparisonIsStaticallyKnown\", 0, expr, Roles.EXPRESSION.create(expr),\n                            LEFT_OPERAND.createFromConst(left), RIGHT_OPERAND.createFromConst(right), Roles.OPERATION\n                                    .create(expr), RESULT.create(result.toString()));\n                    } else if (!deadCode.isExceptional) {\n                        mc.report(\"ResultOfComparisonIsStaticallyKnownDeadCode\", 0, expr,\n                            Roles.EXPRESSION.create(expr), LEFT_OPERAND.createFromConst(left), RIGHT_OPERAND\n                                    .createFromConst(right), Roles.OPERATION.create(expr), Roles.DEAD_CODE_LOCATION.create(\n                                mc, deadCode.startExpr), RESULT.create(result.toString()));\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/LockProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Role.TypeRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Multithreading\", name = \"IncorrectConcurrentMethod\", maxScore = 70)\npublic class LockProblems {\n    private static final TypeRole TARGET = TypeRole.forName(\"TARGET\");\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if (expr.getCode() != AstCode.InvokeVirtual)\n            return;\n        MethodReference mr = (MethodReference) expr.getOperand();\n        String name = mr.getName();\n        if (!Types.isObject(mr.getDeclaringType()) || (!name.equals(\"wait\") && !name.startsWith(\"notify\")))\n            return;\n        TypeReference type = ValuesFlow.reduceType(expr.getArguments().get(0));\n        if (type == null || !type.getInternalName().startsWith(\"java/util/concurrent/\"))\n            return;\n        TypeDefinition target = type.resolve();\n        if (target == null || !target.isPublic())\n            return;\n        MethodDefinition replacement = findReplacement(name, target);\n        if(replacement != null) {\n            mc.report(\"IncorrectConcurrentMethod\", 0, expr, TARGET.create(target), Roles.REPLACEMENT_METHOD.create(replacement));\n        }\n    }\n\n    private static MethodDefinition findReplacement(String name, TypeDefinition target) {\n        for (MethodDefinition md : target.getDeclaredMethods()) {\n            if (!md.isPublic() || !md.getSignature().equals(\"()V\"))\n                continue;\n            if (name.equals(\"wait\") && md.getName().equals(\"await\"))\n                return md;\n            if (name.equals(\"notify\") && (md.getName().equals(\"signal\") || md.getName().equals(\"countDown\")))\n                return md;\n            if (name.equals(\"notifyAll\") && (md.getName().equals(\"signalAll\") || md.getName().equals(\"countDown\")))\n                return md;\n        }\n        return null;\n\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/MinValueHandling.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"AbsoluteValueOfHashCode\", maxScore=60)\n@WarningDefinition(category=\"Correctness\", name=\"AbsoluteValueOfRandomInt\", maxScore=55)\npublic class MinValueHandling {\n    @AstVisitor\n    public void visit(Node node, NodeChain chain, MethodContext mc) {\n        if(Nodes.isOp(node, AstCode.Rem)) {\n            Node body = Nodes.getChild(node, 0);\n            Object modulus = Nodes.getConstant(Nodes.getChild(node, 1));\n            int priority = 0;\n            if((modulus instanceof Integer || modulus instanceof Long) && Long.bitCount(((Number)modulus).longValue()) == 1) {\n                priority += 40;\n            }\n            checkAbs(mc, body, null, priority, true);\n        } else if(chain != null) {\n            checkAbs(mc, node, chain.getNode(), 10, false);\n        }\n    }\n\n\tprivate void checkAbs(MethodContext mc, Node body, Node parent, int priority, boolean forget) {\n\t\tif(Nodes.isOp(body, AstCode.InvokeStatic)) {\n\t\t    MethodReference absCandidate = (MethodReference)((Expression)body).getOperand();\n\t\t    if(absCandidate.getName().equals(\"abs\") && absCandidate.getDeclaringType().getInternalName().equals(\"java/lang/Math\")) {\n\t\t        Node source = Nodes.getChild(body, 0);\n\t\t        if(Nodes.isOp(source, AstCode.InvokeVirtual)) {\n\t\t            MethodReference sourceCall = (MethodReference)((Expression)source).getOperand();\n\t\t            String methodName = sourceCall.getName();\n\t\t\t\t\tString methodSig = sourceCall.getSignature();\n\t\t\t\t\tif((methodName.equals(\"nextInt\") || methodName.equals(\"nextLong\"))\n\t\t                    && (methodSig.equals(\"()I\") || methodSig.equals(\"()J\"))\n\t\t                    && Types.isRandomClass(sourceCall.getDeclaringType())) {\n\t\t\t\t\t    if(methodSig.equals(\"()J\"))\n\t\t\t\t\t        priority += 20;\n\t\t\t\t\t    if(Nodes.isOp(parent, AstCode.Neg)) {\n\t\t\t\t\t        return;\n\t\t\t\t\t    }\n\t\t\t\t\t    if(forget)\n\t\t\t\t\t        mc.forgetLastBug();\n\t\t\t\t\t    mc.report(\"AbsoluteValueOfRandomInt\", priority, source);\n\t\t            } else if(Methods.isHashCodeMethod(sourceCall)) {\n\t\t                if(Nodes.isOp(parent, AstCode.TernaryOp)) {\n\t\t                    Node comparison = Nodes.getChild(parent, 0);\n\t\t                    Integer minValue = Integer.MIN_VALUE;\n\t\t\t\t\t\t\tif(Nodes.isComparison(comparison) && (minValue.equals(Nodes.getConstant(Nodes.getChild(comparison, 0))) ||\n\t\t                            minValue.equals(Nodes.getConstant(Nodes.getChild(comparison, 1)))))\n\t\t                        return;\n\t\t                }\n\t\t                if(Nodes.isOp(parent, AstCode.Neg)) {\n\t\t                    return;\n\t\t                }\n                        if(forget)\n                            mc.forgetLastBug();\n\t\t                mc.report(\"AbsoluteValueOfHashCode\", priority, source);\n\t\t            }\n\t\t        }\n\t\t    }\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/MutableServletField.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningAnnotation.Location;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category = \"Multithreading\", name = \"MutableServletField\", maxScore = 40)\npublic class MutableServletField {\n    static class MethodLocation {\n        MethodDefinition md;\n        Location loc;\n\n        public MethodLocation(MethodDefinition md, Location loc) {\n            this.md = md;\n            this.loc = loc;\n        }\n        \n        public WarningAnnotation<?>[] getAnnotations() {\n            WarningAnnotation<?>[] anno = {Roles.METHOD.create(md), Roles.LOCATION.create(loc)};\n            return anno;\n        }\n    }\n    \n    private final Set<FieldDefinition> reportedFields = new HashSet<>();\n    \n    @ClassVisitor\n    public boolean checkClass(TypeDefinition td) {\n        return Types.isInstance(td, \"javax/servlet/GenericServlet\") && !Types.isInstance(td, \"javax/servlet/SingleThreadModel\");\n    }\n    \n    @MethodVisitor\n    public boolean checkMethod(MethodDefinition md) {\n        return !md.isConstructor() && !md.getName().equals(\"init\") && !md.getName().equals(\"destroy\") && !Flags.testAny(md.getFlags(), Flags.SYNCHRONIZED);\n    }\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visitCode(Expression expr, NodeChain nc, MethodContext mc, MethodDefinition md, TypeDefinition td) {\n        if(expr.getCode() == AstCode.PutField) {\n            FieldDefinition fd = ((FieldReference) expr.getOperand()).resolve();\n            if (fd.getDeclaringType().isEquivalentTo(td) && !nc.isSynchronized() && !Flags.testAny(fd.getFlags(),\n                Flags.VOLATILE) && reportedFields.add(fd)) {\n                mc.report(\"MutableServletField\", 0, expr);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/Naming.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\n\nimport one.util.huntbugs.registry.ClassContext;\nimport one.util.huntbugs.registry.FieldContext;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.FieldVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.AccessLevel;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Role.StringRole;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"CodeStyle\", name = \"BadNameOfMethod\", maxScore = 30)\n@WarningDefinition(category = \"CodeStyle\", name = \"BadNameOfMethodSameAsConstructor\", maxScore = 70)\n@WarningDefinition(category = \"CodeStyle\", name = \"BadNameOfField\", maxScore = 30)\n@WarningDefinition(category = \"CodeStyle\", name = \"BadNameOfClass\", maxScore = 30)\n@WarningDefinition(category = \"CodeStyle\", name = \"BadNameOfClassException\", maxScore = 40)\n@WarningDefinition(category = \"CodeStyle\", name = \"BadNameOfClassSameAsSuperclass\", maxScore = 45)\n@WarningDefinition(category = \"CodeStyle\", name = \"BadNameOfClassSameAsInterface\", maxScore = 45)\n@WarningDefinition(category = \"Correctness\", name = \"BadNameOfMethodMistake\", maxScore = 60)\n@WarningDefinition(category = \"BadPractice\", name = \"BadNameOfMethodFutureKeyword\", maxScore = 70)\n@WarningDefinition(category = \"BadPractice\", name = \"BadNameOfFieldFutureKeyword\", maxScore = 70)\npublic class Naming {\n    private static final StringRole JAVA_VERSION = StringRole.forName(\"JAVA_VERSION\");\n    \n    @ClassVisitor\n    public void visitClass(TypeDefinition td, ClassContext cc) {\n        if (td.isAnonymous() || td.isSynthetic())\n            return;\n        String name = td.getSimpleName();\n        if (Character.isLetter(name.charAt(0)) && !Character.isUpperCase(name.charAt(0)) && name.indexOf('_') == -1) {\n            cc.report(\"BadNameOfClass\", td.isPublic() ? 0 : 15);\n        }\n        if (name.endsWith(\"Exception\") && !Types.isInstance(td, \"java/lang/Throwable\")) {\n            cc.report(\"BadNameOfClassException\", td.isPublic() ? 0 : 15);\n        }\n        TypeReference superClass = td.getBaseType();\n        if (superClass != null && superClass.getSimpleName().equals(name)) {\n            cc.report(\"BadNameOfClassSameAsSuperclass\", td.isPublic() ? 0 : 15, Roles.SUPERCLASS.create(superClass));\n        }\n        for (TypeReference iface : td.getExplicitInterfaces()) {\n            if (iface.getSimpleName().equals(name)) {\n                cc.report(\"BadNameOfClassSameAsInterface\", td.isPublic() ? 0 : 15, Roles.INTERFACE.create(iface));\n            }\n        }\n    }\n\n    @MethodVisitor\n    public void visitMethod(MethodDefinition md, TypeDefinition td, MethodContext mc) {\n        if (badMethodName(md.getName())) {\n            if (Types.isInstance(td, \"org/eclipse/osgi/util/NLS\"))\n                return;\n            // javacc generated methods\n            if (td.getName().equals(\"SimpleCharStream\")\n                && (md.getName().equals(\"ReInit\") || md.getName().equals(\"BeginToken\") || md.getName().equals(\"Done\")\n                    || md.getName().equals(\"GetSuffix\") || md.getName().equals(\"GetImage\")))\n                return;\n            if (td.getName().endsWith(\"TokenManager\") && md.getName().equals(\"ReInit\"))\n                return;\n            int priority = 0;\n            if (!td.isPublic())\n                priority += 20;\n            else {\n                if (td.isFinal())\n                    priority += 3;\n                if (md.isProtected())\n                    priority += 3;\n                else if (md.isPackagePrivate())\n                    priority += 6;\n                else if (md.isPrivate())\n                    priority += 10;\n            }\n            mc.report(\"BadNameOfMethod\", priority);\n        }\n        String javaVersion = getFutureKeywordVersion(md.getName());\n        if (javaVersion != null) {\n            mc.report(\"BadNameOfMethodFutureKeyword\", AccessLevel.of(md).select(0, 10, 20, 30), JAVA_VERSION.create(javaVersion));\n        }\n        if (!md.isStatic() && md.isPublic()) {\n            MemberInfo mi = getMistakeFix(md);\n            if (mi != null) {\n                mc.report(\"BadNameOfMethodMistake\", md.isDeprecated() ? 20 : 0, Roles.REPLACEMENT_METHOD.create(mi));\n            }\n        }\n    }\n\n    private String getFutureKeywordVersion(String name) {\n        switch(name) {\n        case \"strictfp\":\n            return \"Java 1.2\";\n        case \"assert\":\n            return \"Java 1.4\";\n        case \"enum\":\n            return \"Java 1.5\";\n        case \"_\":\n            return \"Java 9\";\n        default:\n            return null;\n        }\n    }\n\n    @AstVisitor(nodes = AstNodes.ROOT)\n    public void checkSameAsConstructor(Block root, MethodDefinition md, TypeDefinition td, MethodContext mc) {\n        if (md.getName().equals(td.getSimpleName()) && md.getReturnType().isVoid() && !md.isDeprecated()) {\n            int priority = 0;\n            if (root.getBody().isEmpty()) {\n                priority += 20;\n            } else if (root.getBody().size() == 1 && Nodes.isOp(root.getBody().get(0), AstCode.AThrow)) {\n                priority += 40;\n            }\n            if (td.getDeclaredMethods().stream().anyMatch(\n                m -> m.isConstructor() && m.getErasedSignature().equals(md.getErasedSignature()))) {\n                priority += 10;\n            }\n            mc.report(\"BadNameOfMethodSameAsConstructor\", priority);\n        }\n    }\n\n    @FieldVisitor\n    public void visitField(FieldDefinition fd, FieldContext fc) {\n        if (badFieldName(fd)) {\n            fc.report(\"BadNameOfField\", AccessLevel.of(fd).select(0, 10, 15, 20));\n        }\n        String javaVersion = getFutureKeywordVersion(fd.getName());\n        if (javaVersion != null) {\n            fc.report(\"BadNameOfFieldFutureKeyword\", AccessLevel.of(fd).select(0, 10, 20, 30), JAVA_VERSION.create(javaVersion));\n        }\n    }\n\n    private MemberInfo getMistakeFix(MethodDefinition md) {\n        if (md.getName().equals(\"hashcode\") && md.getSignature().equals(\"()I\")) {\n            return new MemberInfo(\"java/lang/Object\", \"hashCode\", md.getSignature());\n        }\n        if (md.getName().equals(\"tostring\") && md.getSignature().equals(\"()Ljava/lang/String;\")) {\n            return new MemberInfo(\"java/lang/Object\", \"toString\", md.getSignature());\n        }\n        if (md.getName().equals(\"equal\") && md.getSignature().equals(\"(Ljava/lang/Object;)Z\")) {\n            return new MemberInfo(\"java/lang/Object\", \"equals\", md.getSignature());\n        }\n        return null;\n    }\n\n    private boolean badMethodName(String mName) {\n        return mName.length() >= 2 && Character.isLetter(mName.charAt(0)) && !Character.isLowerCase(mName.charAt(0))\n            && Character.isLetter(mName.charAt(1)) && Character.isLowerCase(mName.charAt(1))\n            && mName.indexOf('_') == -1;\n    }\n\n    private boolean badFieldName(FieldDefinition fd) {\n        String fieldName = fd.getName();\n        return !fd.isFinal() && fieldName.length() > 1 && Character.isLetter(fieldName.charAt(0))\n            && !Character.isLowerCase(fieldName.charAt(0)) && fieldName.indexOf('_') == -1\n            && Character.isLetter(fieldName.charAt(1)) && Character.isLowerCase(fieldName.charAt(1));\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/NegativeRemainder.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"HashCodeRemainder\", maxScore=80)\n@WarningDefinition(category=\"Correctness\", name=\"RandomIntRemainder\", maxScore=80)\npublic class NegativeRemainder {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        switch(expr.getCode()) {\n        case StoreElement:\n        case LoadElement:\n            check(Exprs.getChild(expr, 1), mc);\n            break;\n        case InvokeInterface:\n        case InvokeVirtual:\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if(Types.isInstance(mr.getDeclaringType(), \"java/util/List\")) {\n                if((mr.getName().equals(\"add\") || mr.getName().equals(\"set\")) && mr.getSignature().startsWith(\"(I\") ||\n                        (mr.getName().equals(\"get\") || mr.getName().equals(\"remove\")) && mr.getSignature().startsWith(\"(I)\"))\n                    check(Exprs.getChild(expr, 1), mc);\n            }\n            break;\n        default:\n        }\n    }\n\n    private void check(Expression expr, MethodContext mc) {\n        if(expr.getCode() == AstCode.Rem) {\n            Expression target = Exprs.getChild(expr, 0);\n            if(target.getCode() == AstCode.InvokeVirtual) {\n                MethodReference mr = (MethodReference) target.getOperand();\n                if(Methods.isHashCodeMethod(mr)) {\n                    mc.report(\"HashCodeRemainder\", 0, target);\n                } else if(Types.isRandomClass(mr.getDeclaringType()) && mr.getName().equals(\"nextInt\") && mr.getSignature().equals(\"()I\")) {\n                    mc.report(\"RandomIntRemainder\", 0, target);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/NewGetClass.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.warning.Role.TypeRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Performance\", name=\"NewForGetClass\", maxScore=50)\npublic class NewGetClass {\n    private static final TypeRole OBJECT_TYPE = TypeRole.forName(\"OBJECT_TYPE\");\n\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression node, MethodContext ctx) {\n        if(node.getCode() == AstCode.InvokeVirtual) {\n            MethodReference ref = (MethodReference) node.getOperand();\n            if (Methods.isGetClass(ref) && node.getArguments().get(0).getCode() == AstCode.InitObject) {\n                ctx.report(\"NewForGetClass\", 0, node, OBJECT_TYPE.create(ref.getDeclaringType()));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/NoRuntimeRetention.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.lang.annotation.RetentionPolicy;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.db.DeclaredAnnotations;\nimport one.util.huntbugs.db.DeclaredAnnotations.DeclaredAnnotation;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.TypeRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"AnnotationNoRuntimeRetention\", maxScore = 75)\npublic class NoRuntimeRetention {\n    private static final TypeRole ANNOTATION = TypeRole.forName(\"ANNOTATION\");\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc, DeclaredAnnotations da) {\n        if (expr.getCode() == AstCode.InvokeVirtual && expr.getArguments().size() == 2) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if ((mr.getDeclaringType().getInternalName().startsWith(\"java/lang/reflect/\") || mr.getDeclaringType()\n                    .getInternalName().equals(\"java/lang/Class\")) && mr.getName().contains(\"Annotation\")) {\n                Object constant = Nodes.getConstant(expr.getArguments().get(1));\n                if (constant instanceof TypeReference) {\n                    TypeReference tr = (TypeReference) constant;\n                    DeclaredAnnotation annot = da.get(tr);\n                    if (annot != null && annot.getPolicy() != RetentionPolicy.RUNTIME) {\n                        mc.report(\"AnnotationNoRuntimeRetention\", 0, expr, ANNOTATION.create(tr));\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/NonShortCircuit.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Equi;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\n\n@WarningDefinition(category = \"CodeStyle\", name = \"NonShortCircuit\", maxScore = 50)\n@WarningDefinition(category = \"Correctness\", name = \"NonShortCircuitDangerous\", maxScore = 80)\npublic class NonShortCircuit {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visitNode(Expression node, NodeChain nc, MethodContext ctx) {\n        if(node.getCode() == AstCode.And || node.getCode() == AstCode.Or) {\n            if (Nodes.isOp(nc.getNode(), AstCode.Store) || Nodes.isOp(nc.getNode(), AstCode.StoreElement)\n                || Nodes.isOp(nc.getNode(), AstCode.CompoundAssignment) || Nodes.isOp(nc.getNode(), AstCode.PutField)\n                || Nodes.isOp(nc.getNode(), AstCode.PutStatic))\n                return;\n            Expression left = node.getArguments().get(0);\n            Expression right = node.getArguments().get(1);\n            WarningAnnotation<String> op = Roles.OPERATION.create(node);\n            WarningAnnotation<String> repl = Roles.REPLACEMENT_STRING.create(op.getValue()+op.getValue());\n            WarningAnnotation<?>[] anno = {op, repl, Roles.LEFT_ARGUMENT.create(left), Roles.RIGHT_ARGUMENT.create(right)};\n            if(left.getInferredType().getSimpleType() == JvmType.Boolean &&\n                    right.getInferredType().getSimpleType() == JvmType.Boolean) {\n                if(left.getCode() == AstCode.InstanceOf || Nodes.isNullCheck(left)) {\n                    Expression target = left.getArguments().get(\n                        left.getCode() == AstCode.InstanceOf\n                            || left.getArguments().get(1).getCode() == AstCode.AConstNull ? 0 : 1);\n                    List<Expression> list = Exprs.stream(right).filter(e -> Equi.equiExpressions(e, target)).collect(Collectors.toList());\n                    if(!list.isEmpty()) {\n                        if(list.stream().flatMap(e -> Inf.BACKLINK.findUsages(e).stream()).anyMatch(e -> Nodes.isInvoke(e) || e.getCode() == AstCode.GetField || e.getCode() == AstCode.CheckCast)) {\n                            ctx.report(\"NonShortCircuitDangerous\", 0, node, anno);\n                            return;\n                        }\n                    }\n                }\n                if (!Inf.PURITY.isSideEffectFree(right))\n                    ctx.report(\"NonShortCircuitDangerous\", 20, node, anno);\n                else {\n                    int priority = 0;\n                    if(Nodes.estimateCodeSize(node) < 4)\n                        priority = 10;\n                    ctx.report(\"NonShortCircuit\", priority, node, anno);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/NullCheck.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.CodeBlock;\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.CFG.EdgeType;\nimport one.util.huntbugs.flow.Nullness.NullState;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.Role.ExpressionRole;\n\n/**\n * @author shustkost\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"NullDereferenceGuaranteed\", maxScore = 90)\n@WarningDefinition(category = \"Correctness\", name = \"NullDereferenceExceptional\", maxScore = 50)\n@WarningDefinition(category = \"Correctness\", name = \"NullDereferencePossible\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"ImpossibleInstanceOfNull\", maxScore = 50)\n@WarningDefinition(category = \"RedundantCode\", name = \"RedundantNullCheck\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"RedundantNullCheckNull\", maxScore = 50)\n@WarningDefinition(category = \"RedundantCode\", name = \"RedundantNullCheckDeref\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"RedundantNullCheckChecked\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"RedundantComparisonNull\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"RedundantComparisonNullNonNull\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"RedundantEqualsNullCheck\", maxScore = 60)\npublic class NullCheck {\n    private static final ExpressionRole NONNULL_EXPRESSION = ExpressionRole.forName(\"NONNULL_EXPRESSION\");\n    private static final ExpressionRole NULL_EXPRESSION = ExpressionRole.forName(\"NULL_EXPRESSION\");\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc) {\n        switch (expr.getCode()) {\n        case MonitorEnter:\n        case MonitorExit:\n        case PutField:\n        case GetField:\n        case LoadElement:\n        case StoreElement:\n        case InvokeInterface:\n        case InvokeSpecial:\n        case InvokeVirtual: {\n            NullState nullness = Inf.NULL.resolve(expr.getArguments().get(0)).stateAt(mc.getCFG(), expr);\n            String type = null;\n            if(nullness == NullState.NULL) {\n                type = \"NullDereferenceGuaranteed\";\n            } else if(nullness == NullState.NULL_EXCEPTIONAL) {\n                type = \"NullDereferenceExceptional\";\n            } else if(nullness == NullState.NULLABLE) {\n                type = \"NullDereferencePossible\";\n            }\n            if(type != null) {\n                int priority = 0;\n                if (nc.isInTry(\"java/lang/NullPointerException\", \"java/lang/RuntimeException\", \"java/lang/Exception\",\n                    \"java/lang/Throwable\"))\n                    priority += 30;\n                mc.report(type, priority, expr, Roles.EXPRESSION.create(expr), NULL_EXPRESSION.create(\n                    expr.getArguments().get(0)));\n                return;\n            }\n            if(expr.getCode() == AstCode.InvokeVirtual && Methods.isEqualsMethod((MethodReference) expr.getOperand())) {\n                if(Inf.NULL.resolve(expr.getArguments().get(1)).isNull()) {\n                    type = \"RedundantEqualsNullCheck\";\n                    List<WarningAnnotation<?>> anno = new ArrayList<>();\n                    anno.add(Roles.EXPRESSION.create(expr));\n                    anno.add(NULL_EXPRESSION.create(expr.getArguments().get(1)));\n                    CodeBlock deadCode = mc.findDeadCode(expr, EdgeType.TRUE);\n                    int priority;\n                    if (deadCode != null) {\n                        priority = deadCode.isExceptional ? 45 : deadCode.length < 4 ? 5 : 0;\n                        anno.add(Roles.DEAD_CODE_LOCATION.create(mc, deadCode.startExpr));\n                    } else\n                        priority = 30;\n                    mc.report(type, priority, expr, anno);\n                }\n            }\n            break;\n        }\n        case InstanceOf: {\n            if (Inf.NULL.resolve(expr.getArguments().get(0)).isNull()) {\n                CodeBlock deadCode = mc.findDeadCode(expr, EdgeType.TRUE);\n                if (deadCode != null) {\n                    mc.report(\"ImpossibleInstanceOfNull\", deadCode.isExceptional ? 45 : 0, expr.getArguments().get(0),\n                        Roles.DEAD_CODE_LOCATION.create(mc, deadCode.startExpr), Roles.EXPRESSION.create(expr),\n                        NULL_EXPRESSION.create(expr.getArguments().get(0)));\n                } else {\n                    mc.report(\"ImpossibleInstanceOfNull\", 20, expr.getArguments().get(0), Roles.EXPRESSION.create(expr),\n                        NULL_EXPRESSION.create(expr.getArguments().get(0)));\n                }\n            }\n            break;\n        }\n        case CmpNe:\n        case CmpEq: {\n            Expression left = expr.getArguments().get(0);\n            Expression right = expr.getArguments().get(1);\n            NullState leftNull = Inf.NULL.resolve(left).stateAt(null, left);\n            NullState rightNull = Inf.NULL.resolve(right).stateAt(null, right);\n            Expression nullExpr = null;\n            Expression nonNullExpr = null;\n            NullState nonNull = null;\n            if (leftNull.isNull() && rightNull.isNonNull()) {\n                nullExpr = left;\n                nonNullExpr = right;\n                nonNull = rightNull;\n            } else if (rightNull.isNull() && leftNull.isNonNull()) {\n                nullExpr = right;\n                nonNullExpr = left;\n                nonNull = leftNull;\n            }\n            if (nullExpr != null) {\n                String type = \"RedundantNullCheck\";\n                if (nonNull == NullState.NONNULL_CHECKED)\n                    type = \"RedundantNullCheckChecked\";\n                else if (nonNull == NullState.NONNULL_DEREF)\n                    type = \"RedundantNullCheckDeref\";\n                List<WarningAnnotation<?>> anno = new ArrayList<>();\n                anno.add(Roles.EXPRESSION.create(expr));\n                anno.add(NONNULL_EXPRESSION.create(nonNullExpr));\n                if (nullExpr.getCode() != AstCode.AConstNull) {\n                    anno.add(NULL_EXPRESSION.create(nullExpr));\n                    type = \"RedundantComparisonNullNonNull\";\n                }\n                CodeBlock deadCode = mc.findDeadCode(expr, expr.getCode() == AstCode.CmpEq ? EdgeType.TRUE\n                        : EdgeType.FALSE);\n                int priority;\n                if (deadCode != null) {\n                    priority = deadCode.isExceptional ? 45 : deadCode.length < 4 ? 5 : 0;\n                    anno.add(Roles.DEAD_CODE_LOCATION.create(mc, deadCode.startExpr));\n                } else\n                    priority = 30;\n                mc.report(type, priority, expr, anno);\n            }\n            if (leftNull.isNull() && rightNull.isNull()) {\n                String type = \"RedundantNullCheckNull\";\n                List<WarningAnnotation<?>> anno = new ArrayList<>();\n                anno.add(Roles.EXPRESSION.create(expr));\n                if (right.getCode() == AstCode.AConstNull) {\n                    anno.add(NULL_EXPRESSION.create(left));\n                } else if (left.getCode() == AstCode.AConstNull) {\n                    anno.add(NULL_EXPRESSION.create(right));\n                } else {\n                    type = \"RedundantComparisonNull\";\n                }\n                int priority;\n                CodeBlock deadCode = mc.findDeadCode(expr, expr.getCode() == AstCode.CmpEq ? EdgeType.FALSE\n                        : EdgeType.TRUE);\n                if (deadCode != null) {\n                    priority = deadCode.isExceptional ? 45 : deadCode.length < 4 ? 5 : 0;\n                    anno.add(Roles.DEAD_CODE_LOCATION.create(mc, deadCode.startExpr));\n                } else\n                    priority = 20;\n                mc.report(type, priority, expr, anno);\n            }\n            break;\n        }\n        default:\n            break;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/NumberConstructor.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Performance\", name = \"NumberConstructor\", maxScore = 45)\n@WarningDefinition(category = \"Performance\", name = \"BooleanConstructor\", maxScore = 55)\npublic class NumberConstructor {\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext ctx, MethodDefinition md) {\n        if (expr.getCode() == AstCode.InitObject && expr.getArguments().size() == 1) {\n            MethodReference ctor = (MethodReference) expr.getOperand();\n            if (ctor.getDeclaringType().getPackageName().equals(\"java.lang\")) {\n                String simpleName = ctor.getDeclaringType().getSimpleName();\n                WarningAnnotation<MemberInfo> replacement = Roles.REPLACEMENT_METHOD.create(ctor.getDeclaringType()\n                        .getInternalName(), \"valueOf\", ctor.getSignature().replaceFirst(\"V$\", \"L\" + ctor\n                                .getDeclaringType().getInternalName() + \";\"));\n                int priority = 0;\n                if(md.isTypeInitializer()) {\n                    // Static field initializer: only one object is created\n                    // not a big performance problem and probably intended\n                    Set<Expression> usages = Inf.BACKLINK.findUsages(expr);\n                    if(usages.size() == 1 && usages.iterator().next().getCode() == AstCode.PutStatic) {\n                        priority = 15;\n                    }\n                }\n                if (simpleName.equals(\"Boolean\")) {\n                    ctx.report(\"BooleanConstructor\", priority, expr, replacement);\n                } else if (simpleName.equals(\"Integer\") || simpleName.equals(\"Long\") || simpleName.equals(\"Short\")\n                    || simpleName.equals(\"Byte\") || simpleName.equals(\"Character\")) {\n                    Object val = Nodes.getConstant(expr.getArguments().get(0));\n                    if (val instanceof Number) {\n                        long value = ((Number) val).longValue();\n                        if (value < -128 || value > 127)\n                            priority += simpleName.equals(\"Integer\") ? 15 : 30;\n                        ctx.report(\"NumberConstructor\", priority, expr, Roles.NUMBER.create((Number) val), replacement,\n                            Roles.TARGET_TYPE.create(ctor.getDeclaringType()));\n                    } else {\n                        ctx.report(\"NumberConstructor\", 5, expr, replacement, Roles.TARGET_TYPE.create(ctor\n                                .getDeclaringType()));\n                    }\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/NumericComparison.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.CaseBlock;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Switch;\n\nimport one.util.huntbugs.flow.CFG.EdgeType;\nimport one.util.huntbugs.flow.CodeBlock;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.Role.StringRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"ComparisonWithOutOfRangeValue\", maxScore = 80)\n@WarningDefinition(category = \"RedundantCode\", name = \"SwitchBranchUnreachable\", maxScore = 75)\n@WarningDefinition(category = \"BadPractice\", name = \"CheckForOddnessFailsForNegative\", maxScore = 40)\npublic class NumericComparison {\n    private static final LongRange SHORT_RANGE = new LongRange(Short.MIN_VALUE, Short.MAX_VALUE);\n    private static final LongRange CHAR_RANGE = new LongRange(Character.MIN_VALUE, Character.MAX_VALUE);\n    private static final LongRange LONG_RANGE = new LongRange(Long.MIN_VALUE, Long.MAX_VALUE);\n    private static final LongRange BYTE_RANGE = new LongRange(Byte.MIN_VALUE, Byte.MAX_VALUE);\n    private static final LongRange INT_RANGE = new LongRange(Integer.MIN_VALUE, Integer.MAX_VALUE);\n\n    private static final StringRole RESULT = StringRole.forName(\"RESULT\");\n\n    static class LongRange {\n        final long minValue, maxValue;\n        final boolean invert;\n\n        public LongRange(AstCode code, long constant) {\n            boolean invert = false;\n            switch (code) {\n            case CmpNe:\n                invert = true;\n                // passthru\n            case CmpEq:\n                minValue = maxValue = constant;\n                break;\n            case CmpLt:\n                minValue = Long.MIN_VALUE;\n                maxValue = constant - 1;\n                break;\n            case CmpLe:\n                minValue = Long.MIN_VALUE;\n                maxValue = constant;\n                break;\n            case CmpGt:\n                minValue = constant + 1;\n                maxValue = Long.MAX_VALUE;\n                break;\n            case CmpGe:\n                minValue = constant;\n                maxValue = Long.MAX_VALUE;\n                break;\n            default:\n                throw new InternalError(\"Unexpected code: \" + code);\n            }\n            this.invert = invert;\n        }\n\n        LongRange absInt() {\n            if (minValue <= Integer.MIN_VALUE || minValue >= 0)\n                return this;\n            if (maxValue <= 0)\n                return new LongRange(-maxValue, -minValue);\n            return new LongRange(0, Math.max(-minValue, maxValue));\n        }\n\n        LongRange union(LongRange other) {\n            if (invert || other.invert)\n                throw new IllegalStateException();\n            return new LongRange(Math.min(minValue, other.minValue), Math.max(maxValue, other.maxValue));\n        }\n\n        LongRange(long min, long max) {\n            minValue = min;\n            maxValue = max;\n            invert = false;\n        }\n\n        boolean isTrueEmpty(LongRange realRange) {\n            if (invert)\n                return new LongRange(minValue, maxValue).isFalseEmpty(realRange);\n            return realRange.minValue > maxValue || realRange.maxValue < minValue;\n        }\n\n        boolean isFalseEmpty(LongRange realRange) {\n            if (invert)\n                return new LongRange(minValue, maxValue).isTrueEmpty(realRange);\n            return realRange.minValue >= minValue && realRange.maxValue <= maxValue;\n        }\n    }\n\n    @AstVisitor(nodes = AstNodes.ALL)\n    public void visit(Node node, MethodContext mc) {\n        if (Nodes.isComparison(node)) {\n            Expression expr = (Expression) node;\n            JvmType jvmType;\n            AstCode code = expr.getCode();\n            Expression arg;\n            Object left = Nodes.getConstant(expr.getArguments().get(0));\n            Object right = Nodes.getConstant(expr.getArguments().get(1));\n            long constant;\n            if (left instanceof Number) {\n                if (right instanceof Number) {\n                    return; // Will be covered by KnownComparison\n                }\n                constant = ((Number) left).longValue();\n                arg = Exprs.getChild(expr, 1);\n                jvmType = getIntegralType(expr.getArguments().get(1));\n                switch (code) {\n                case CmpGe:\n                    code = AstCode.CmpLe;\n                    break;\n                case CmpGt:\n                    code = AstCode.CmpLt;\n                    break;\n                case CmpLe:\n                    code = AstCode.CmpGe;\n                    break;\n                case CmpLt:\n                    code = AstCode.CmpGt;\n                    break;\n                default:\n                }\n            } else {\n                if (!(right instanceof Number))\n                    return;\n                constant = ((Number) right).longValue();\n                arg = Exprs.getChild(expr, 0);\n                jvmType = getIntegralType(expr.getArguments().get(0));\n            }\n            if (jvmType == null)\n                return;\n\n            checkRem2Eq1(mc, jvmType, code, arg, constant);\n\n            LongRange cmpRange = new LongRange(code, constant);\n            LongRange realRange = getExpressionRange(jvmType, arg);\n            if (realRange == null)\n                return;\n            Boolean result = null;\n            if (cmpRange.isTrueEmpty(realRange)) {\n                result = false;\n            } else if (cmpRange.isFalseEmpty(realRange)) {\n                result = true;\n            }\n            if (result == null || Exprs.isAssertion(expr))\n                return;\n            int priority = 0;\n            CodeBlock deadCode = mc.findDeadCode(expr, result ? EdgeType.FALSE : EdgeType.TRUE);\n            List<WarningAnnotation<?>> anno = new ArrayList<>(Arrays.asList(Roles.OPERATION.create(code), Roles.NUMBER\n                    .create(constant), Roles.MIN_VALUE.create(realRange.minValue), Roles.MAX_VALUE.create(\n                        realRange.maxValue), RESULT.create(result.toString())));\n            if (deadCode == null) {\n                priority += 20;\n                if (realRange.minValue == constant || realRange.maxValue == constant)\n                    priority += 15;\n                else if (Math.abs(realRange.minValue - constant) == 1 || Math.abs(realRange.maxValue - constant) == 1)\n                    priority += 5;\n            } else {\n                if(deadCode.isExceptional)\n                    priority += 55;\n                else if(deadCode.length < 4)\n                    priority += 5;\n                anno.add(Roles.DEAD_CODE_LOCATION.create(mc, deadCode.startExpr));\n            }\n            mc.report(\"ComparisonWithOutOfRangeValue\", priority, expr, anno);\n        } else if (node instanceof Switch) {\n            Switch switchNode = (Switch) node;\n            Expression condition = switchNode.getCondition();\n            JvmType type = condition.getInferredType() == null ? JvmType.Integer\n                    : condition.getInferredType().getSimpleType();\n            LongRange realRange = getExpressionRange(type, condition);\n            if (realRange == null || realRange.minValue <= Integer.MIN_VALUE && realRange.maxValue >= Integer.MAX_VALUE)\n                return;\n            for (CaseBlock block : switchNode.getCaseBlocks()) {\n                block.getValues().stream().filter(val -> new LongRange(AstCode.CmpEq, val).isTrueEmpty(realRange))\n                        .findFirst().ifPresent(val -> {\n                            mc.report(\"SwitchBranchUnreachable\", 0, block, Roles.NUMBER.create(val), Roles.MIN_VALUE\n                                    .create(realRange.minValue), Roles.MAX_VALUE.create(realRange.maxValue));\n                        });\n            }\n        }\n    }\n\n    private JvmType getIntegralType(Expression expression) {\n        TypeReference type = expression.getInferredType();\n        if (type == null)\n            return null;\n        JvmType jvmType = type.getSimpleType();\n        if (!jvmType.isIntegral())\n            return null;\n        if (jvmType == JvmType.Integer || jvmType == JvmType.Long)\n            return jvmType;\n        // Fix procyon type inference\n        switch (expression.getCode()) {\n        case Add:\n        case Sub:\n        case Div:\n        case Rem:\n        case Mul:\n        case Shr:\n        case Shl:\n        case UShr:\n        case Neg:\n            return JvmType.Integer;\n        default:\n            return jvmType;\n        }\n    }\n\n    private void checkRem2Eq1(MethodContext mc, JvmType jvmType, AstCode code, Expression arg, long constant) {\n        if (constant == 1 && (code == AstCode.CmpEq || code == AstCode.CmpNe) && arg.getCode() == AstCode.Rem && Integer\n                .valueOf(2).equals(Nodes.getConstant(arg.getArguments().get(1)))) {\n            Expression remInput = Exprs.getChild(arg, 0);\n            if (remInput.getCode() == AstCode.InvokeStatic) {\n                MethodReference mr = (MethodReference) remInput.getOperand();\n                if (mr.getName().equals(\"abs\") && mr.getDeclaringType().getInternalName().equals(\"java/lang/Math\")) {\n                    return;\n                }\n            }\n            if (getExpressionRange(jvmType, remInput).minValue < 0) {\n                mc.report(\"CheckForOddnessFailsForNegative\", 0, arg, Roles.OPERATION.create(code),\n                    Roles.REPLACEMENT_STRING.create(code == AstCode.CmpEq ? \"!=\" : \"==\"));\n            }\n        }\n    }\n\n    private static LongRange getExpressionRange(JvmType type, Expression arg) {\n        return getExpressionRange(type, arg, new HashSet<>());\n    }\n\n    private static LongRange getExpressionRange(JvmType type, Expression arg, Set<Expression> visited) {\n        return ValuesFlow.reduce(arg, e -> {\n            if (!visited.add(e))\n                return getTypeRange(type);\n            Object constant = Nodes.getConstant(e);\n            if (constant instanceof Integer || constant instanceof Long) {\n                long val = ((Number) constant).longValue();\n                return new LongRange(val, val);\n            }\n            if (type == JvmType.Integer)\n                return intRange(e, visited);\n            if (e.getCode() == AstCode.I2L)\n                return intRange(Exprs.getChild(e, 0), visited);\n            return getTypeRange(type);\n        }, (r1, r2) -> r1 == null || r2 == null ? null : r1.union(r2), r -> r == getTypeRange(type));\n    }\n\n    private static LongRange getTypeRange(JvmType type) {\n        switch (type) {\n        case Integer:\n            return INT_RANGE;\n        case Byte:\n            return BYTE_RANGE;\n        case Long:\n            return LONG_RANGE;\n        case Character:\n            return CHAR_RANGE;\n        case Short:\n            return SHORT_RANGE;\n        default:\n            return null;\n        }\n    }\n\n    private static LongRange intRange(Expression arg, Set<Expression> visited) {\n        switch (arg.getCode()) {\n        case ArrayLength:\n            return new LongRange(0, Integer.MAX_VALUE);\n        case And: {\n            LongRange r1 = getExpressionRange(JvmType.Integer, Exprs.getChild(arg, 0), visited);\n            LongRange r2 = getExpressionRange(JvmType.Integer, Exprs.getChild(arg, 1), visited);\n            int maxBit1 = r1.minValue < 0 ? 0x80000000 : Integer.highestOneBit((int) r1.maxValue);\n            int maxBit2 = r2.minValue < 0 ? 0x80000000 : Integer.highestOneBit((int) r2.maxValue);\n            int totalMax = ((maxBit1 << 1) - 1) & ((maxBit2 << 1) - 1);\n            if (totalMax >= 0)\n                return new LongRange(0, totalMax);\n            break;\n        }\n        case Rem: {\n            LongRange remRange = getExpressionRange(JvmType.Integer, Exprs.getChild(arg, 1), visited).absInt();\n            if (remRange.minValue < 0 || remRange.maxValue == 0)\n                break;\n            LongRange divRange = getExpressionRange(JvmType.Integer, Exprs.getChild(arg, 0), visited);\n            if (divRange.minValue >= 0)\n                return new LongRange(0, remRange.maxValue - 1);\n            return new LongRange(1 - remRange.maxValue, remRange.maxValue - 1);\n        }\n        case Shr: {\n            Object constant = Nodes.getConstant(arg.getArguments().get(1));\n            if (constant instanceof Integer) {\n                int shrOp = ((int) constant) & 0x1F;\n                int bits = 31 - shrOp;\n                int max = (1 << bits) - 1;\n                return new LongRange(-max - 1, max);\n            }\n            break;\n        }\n        case UShr: {\n            Object constant = Nodes.getConstant(arg.getArguments().get(1));\n            if (constant instanceof Integer) {\n                int shrOp = ((int) constant) & 0x1F;\n                if (shrOp != 0) {\n                    int bits = 32 - shrOp;\n                    int max = (1 << bits);\n                    return new LongRange(0, max - 1);\n                }\n            }\n            break;\n        }\n        case InvokeStatic:\n        case InvokeSpecial: {\n            MethodReference mr = (MethodReference) arg.getOperand();\n            return getTypeRange(mr.getReturnType().getSimpleType());\n        }\n        case InvokeVirtual:\n        case InvokeInterface: {\n            MethodReference mr = (MethodReference) arg.getOperand();\n            if (mr.getName().equals(\"size\") && mr.getSignature().equals(\"()I\")) {\n                if (Types.isInstance(mr.getDeclaringType(), \"java/util/Collection\") || Types.isInstance(mr\n                        .getDeclaringType(), \"java/util/Map\")) {\n                    return new LongRange(0, Integer.MAX_VALUE);\n                }\n            }\n            if (mr.getName().equals(\"nextInt\") && mr.getSignature().equals(\"(I)I\") && Types.isRandomClass(mr\n                    .getDeclaringType())) {\n                LongRange argRange = getExpressionRange(JvmType.Integer, Exprs.getChild(arg, 1), visited);\n                if (argRange.maxValue > 0) {\n                    return new LongRange(0, argRange.maxValue - 1);\n                }\n            }\n            return getTypeRange(mr.getReturnType().getSimpleType());\n        }\n        default:\n        }\n        return INT_RANGE;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/NumericPromotion.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.math.BigInteger;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.LocationRole;\nimport one.util.huntbugs.warning.Role.StringRole;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningAnnotation.Location;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"IntegerMultiplicationPromotedToLong\", maxScore = 65)\n@WarningDefinition(category = \"Correctness\", name = \"IntegerDivisionPromotedToFloat\", maxScore = 65)\n@WarningDefinition(category = \"Correctness\", name = \"IntegerPromotionInCeilOrRound\", maxScore = 65)\npublic class NumericPromotion {\n    private static final StringRole SOURCE_TYPE = StringRole.forName(\"SOURCE_TYPE\");\n    private static final StringRole TARGET_TYPE = StringRole.forName(\"TARGET_TYPE\");\n    private static final LocationRole DIVISION_AT = LocationRole.forName(\"DIVISION_AT\");\n    \n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc, MethodDefinition md) {\n        if (expr.getCode() == AstCode.I2L) {\n            Expression arg = expr.getArguments().get(0);\n            if (arg.getCode() == AstCode.Mul) {\n                BigInteger res = getMultiplicationConstant(arg);\n                int priority = 0;\n                if (Methods.isHashCodeMethod(md))\n                    priority += 30;\n                try {\n                    int factor = Math.abs(res.intValueExact());\n                    if (factor > 1) {\n                        if (factor <= 4) {\n                            priority += 50;\n                        } else if (factor <= 100) {\n                            priority += 40;\n                        } else if (factor <= 10000) {\n                            priority += 30;\n                        } else if (factor <= 60 * 60 * 1000) {\n                            priority += 20;\n                        } else\n                            priority += 10;\n                        mc.report(\"IntegerMultiplicationPromotedToLong\", priority, expr, Roles.NUMBER.create(res));\n                    }\n                } catch (ArithmeticException e) {\n                    mc.report(\"IntegerMultiplicationPromotedToLong\", priority, expr, Roles.NUMBER.create(res));\n                }\n            }\n        }\n        if (Nodes.isToFloatingPointConversion(expr)) {\n            if (Nodes.isOp(nc.getNode(), AstCode.InvokeStatic)) {\n                MethodReference mr = (MethodReference) ((Expression) nc.getNode()).getOperand();\n                if (mr.getDeclaringType().getInternalName().equals(\"java/lang/Math\")\n                    && (mr.getName().equals(\"ceil\") || mr.getName().equals(\"round\"))) {\n                    mc.report(\"IntegerPromotionInCeilOrRound\", 0, nc.getNode(), SOURCE_TYPE.create(getSourceType(expr)), \n                        TARGET_TYPE.create(getTargetType(expr)));\n                    return;\n                }\n            }\n            Expression arg = ValuesFlow.getSource(expr.getArguments().get(0));\n            if (arg.getCode() == AstCode.Div) {\n                if(!Inf.BACKLINK.findTransitiveUsages(arg, true).allMatch(NumericPromotion::isToFloatingPointConversion))\n                    return;\n                Object constant = Nodes.getConstant(arg.getArguments().get(1));\n                int priority = 0;\n                boolean isPowerOfTen = false;\n                if (constant instanceof Number) {\n                    long val = Math.abs(((Number) constant).longValue());\n                    if (val >= 2 && val <= 4)\n                        priority += 10;\n                    else if(isPowerOfTen(val)) {\n                        isPowerOfTen = true;\n                        priority += 15;\n                    }\n                }\n                if (Nodes.isOp(nc.getNode(), AstCode.Div)) {\n                    Expression parent = (Expression) nc.getNode();\n                    if (parent.getArguments().get(0) == expr) {\n                        Object divisor = Nodes.getConstant(parent.getArguments().get(1));\n                        if (divisor instanceof Number) {\n                            long divisorVal = ((Number) divisor).longValue();\n                            if(isPowerOfTen(divisorVal)) {\n                                // some rounding like ((double)a/10)/10;\n                                priority += isPowerOfTen ? 50 : 10;\n                            }\n                            // Lower priority for scenarios like\n                            // ((a*1000)/b)/10.0\n                            Expression divident = arg.getArguments().get(0);\n                            if (divident.getCode() == AstCode.Mul) {\n                                BigInteger multiplier = getMultiplicationConstant(divident);\n                                if ((multiplier.equals(BigInteger.valueOf(divisorVal * 100)) || multiplier\n                                        .equals(BigInteger.valueOf(divisorVal))))\n                                priority += 100;\n                            }\n                        }\n                    }\n                }\n                if (Inf.BACKLINK.findTransitiveUsages(expr, true).allMatch(Nodes::isComparison)) {\n                    priority += 15;\n                }\n                if (Inf.BACKLINK.findTransitiveUsages(expr, true).allMatch(e ->\n                        e.getCode() == AstCode.InvokeStatic && \n                        Methods.is((MethodReference) e.getOperand(), \"java/lang/Math\", \"pow\", \"(DD)D\") &&\n                        Exprs.getChild(e, 1) == expr)) {\n                    priority += 15;\n                }\n                List<WarningAnnotation<?>> anno = new ArrayList<>();\n                anno.add(SOURCE_TYPE.create(getSourceType(expr)));\n                anno.add(TARGET_TYPE.create(getTargetType(expr)));\n                Location divLoc = mc.getLocation(arg);\n                if(divLoc.getSourceLine() != mc.getLocation(expr).getSourceLine())\n                    anno.add(DIVISION_AT.create(divLoc));\n                Object op = expr.getArguments().get(0).getOperand();\n                if(op instanceof Variable) {\n                    anno.add(WarningAnnotation.forVariable((Variable)op));\n                }\n                mc.report(\"IntegerDivisionPromotedToFloat\", priority, expr, anno);\n            }\n        }\n    }\n\n    private boolean isPowerOfTen(long divisorVal) {\n        return divisorVal == 10 || divisorVal == 100 || divisorVal == 1000 || divisorVal == 10000\n            || divisorVal == 100000 || divisorVal == 1000000 || divisorVal == 10000000 || divisorVal == 100000000\n            || divisorVal == 1000000000;\n    }\n\n    private static String getSourceType(Expression expr) {\n        switch(expr.getCode()) {\n        case I2F:\n        case I2D:\n            return \"int\";\n        case L2F:\n        case L2D:\n            return \"long\";\n        default:\n            throw new InternalError(expr.getCode().toString());\n        }\n    }\n\n    private static String getTargetType(Expression expr) {\n        switch(expr.getCode()) {\n        case I2F:\n        case L2F:\n            return \"float\";\n        case I2D:\n        case L2D:\n            return \"double\";\n        default:\n            throw new InternalError(expr.getCode().toString());\n        }\n    }\n    \n    private static BigInteger getMultiplicationConstant(Expression arg) {\n        Expression left = arg.getArguments().get(0);\n        Expression right = arg.getArguments().get(1);\n        Object leftConst = Nodes.getConstant(left);\n        Object rightConst = Nodes.getConstant(right);\n        BigInteger leftOperand = BigInteger.ONE;\n        BigInteger rightOperand = BigInteger.ONE;\n        if (leftConst instanceof Number) {\n            leftOperand = BigInteger.valueOf(((Number) leftConst).longValue());\n        } else if (left.getCode() == AstCode.Mul) {\n            leftOperand = getMultiplicationConstant(left);\n        }\n        if (rightConst instanceof Number) {\n            rightOperand = BigInteger.valueOf(((Number) rightConst).longValue());\n        } else if (right.getCode() == AstCode.Mul) {\n            rightOperand = getMultiplicationConstant(right);\n        }\n        return leftOperand.multiply(rightOperand);\n    }\n    \n    private static boolean isToFloatingPointConversion(Expression expr) {\n        if(Nodes.isToFloatingPointConversion(expr))\n            return true;\n        if(expr.getCode() == AstCode.Neg)\n            return Inf.BACKLINK.findTransitiveUsages(expr, true).allMatch(NumericPromotion::isToFloatingPointConversion);\n        return false;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/RandomUsage.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.Role.TypeRole;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Performance\", name = \"RandomNextIntViaNextDouble\", maxScore = 50)\n@WarningDefinition(category = \"Correctness\", name = \"RandomDoubleToInt\", maxScore = 80)\n@WarningDefinition(category = \"Correctness\", name = \"RandomUsedOnlyOnce\", maxScore = 70)\npublic class RandomUsage {\n    private static final TypeRole RANDOM_TYPE = TypeRole.forName(\"RANDOM_TYPE\");\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression node, MethodContext ctx) {\n        if (node.getCode() == AstCode.D2I) {\n            Expression child = Exprs.getChild(node, 0);\n            if (isRandomDouble(child)) {\n                ctx.report(\"RandomDoubleToInt\", 0, child, getReplacement(((MethodReference) child.getOperand())\n                        .getDeclaringType().getInternalName()));\n            }\n            Expression mul = node.getArguments().get(0);\n            if (mul.getCode() == AstCode.Mul) {\n                mul.getArguments().stream().filter(this::isRandomDouble).findFirst().ifPresent(\n                    expr -> {\n                        int priority = 0;\n                        MethodReference mr = (MethodReference) expr.getOperand();\n                        String type = mr.getDeclaringType().getInternalName();\n                        if (type.equals(\"java/lang/Math\")) {\n                            priority = 20;\n                        }\n                        ctx.report(\"RandomNextIntViaNextDouble\", priority, node, Roles.CALLED_METHOD.create(mr),\n                            getReplacement(type));\n                    });\n            }\n        }\n        checkOnlyOnce(node, ctx);\n    }\n\n    void checkOnlyOnce(Expression node, MethodContext ctx) {\n        if (node.getCode() != AstCode.InvokeVirtual || node.getArguments().get(0).getCode() != AstCode.InitObject)\n            return;\n        MethodReference ctor = (MethodReference) node.getArguments().get(0).getOperand();\n        TypeReference type = ctor.getDeclaringType();\n        if (!Types.isRandomClass(type) || type.getInternalName().equals(\"java/security/SecureRandom\"))\n            return;\n        MethodReference mr = (MethodReference) node.getOperand();\n        if(mr.getReturnType().getPackageName().equals(\"java.util.stream\"))\n            return;\n        if(Inf.BACKLINK.findTransitiveUsages(node, true).allMatch(this::isRandomInit))\n            return;\n        ctx.report(\"RandomUsedOnlyOnce\", 0, node, RANDOM_TYPE.create(type));\n    }\n    \n    private boolean isRandomInit(Expression expr) {\n        if(expr.getCode() == AstCode.InitObject) {\n            MethodReference ctor = (MethodReference) expr.getOperand();\n            // It seems ok to initialize cern.jet.random generators with new Random().nextInt()\n            if(ctor.getDeclaringType().getPackageName().equals(\"cern.jet.random.engine\"))\n                return true;\n        }\n        return false;\n    }\n\n    private WarningAnnotation<MemberInfo> getReplacement(String type) {\n        return Roles.REPLACEMENT_METHOD.create(type.equals(\"java/lang/Math\") ? \"java/util/Random\" : type,\n            \"nextInt\", \"(I)I\");\n    }\n\n    private boolean isRandomDouble(Node node) {\n        if (Nodes.isInvoke(node)) {\n            MethodReference mr = (MethodReference) ((Expression) node).getOperand();\n            if (mr.getSignature().equals(\"()D\")\n                && (Types.isRandomClass(mr.getDeclaringType()) && mr.getName().equals(\"nextDouble\") || mr\n                        .getDeclaringType().getInternalName().equals(\"java/lang/Math\")\n                    && mr.getName().equals(\"random\"))) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/RedundantInterfaces.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.registry.ClassContext;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"CodeStyle\", name = \"RedundantInterface\", maxScore = 20)\npublic class RedundantInterfaces {\n    @ClassVisitor\n    public void visit(TypeDefinition td, ClassContext cc) {\n        TypeDefinition baseType = td.getBaseType().resolve();\n        if(baseType == null || Types.isObject(baseType))\n            return;\n        for(TypeReference tr : td.getExplicitInterfaces()) {\n            if(tr.getInternalName().equals(\"java/io/Serializable\")) {\n                continue;\n            }\n            if(Types.isInstance(baseType, tr)) {\n                cc.report(\"RedundantInterface\", td.isPublic() ? 0 : 10, Roles.INTERFACE.create(tr));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/RedundantStreamCalls.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.function.Predicate;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.ExpressionFormatter;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"RedundantCode\", name=\"RedundantStreamForEach\", maxScore=50)\n@WarningDefinition(category=\"RedundantCode\", name=\"RedundantStreamFind\", maxScore=48)\n@WarningDefinition(category=\"RedundantCode\", name=\"RedundantCollectionStream\", maxScore=48)\n@WarningDefinition(category=\"Performance\", name=\"StreamCountFromCollection\", maxScore=60)\npublic class RedundantStreamCalls {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS, minVersion=8)\n    public void visit(Expression expr, MethodContext mc) {\n        if(expr.getCode() == AstCode.InvokeInterface || expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if(isStreamForEach(mr)) {\n                Expression stream = Exprs.getChild(expr, 0);\n                if(stream.getCode() == AstCode.InvokeInterface || stream.getCode() == AstCode.InvokeVirtual) {\n                    MethodReference mr2 = (MethodReference) stream.getOperand();\n                    if(isCollectionStream(mr2)) {\n                        mc.report(\"RedundantStreamForEach\", 0, expr, Roles.REPLACEMENT_METHOD.create(mr2\n                                .getDeclaringType().getInternalName(), \"forEach\", \"(Ljava/util/function/Consumer;)V\"));\n                    }\n                }\n            }\n            if(isStreamCount(mr)) {\n                Expression stream = Exprs.getChild(expr, 0);\n                if(stream.getCode() == AstCode.InvokeInterface || stream.getCode() == AstCode.InvokeVirtual) {\n                    MethodReference mr2 = (MethodReference) stream.getOperand();\n                    if(isCollectionStream(mr2)) {\n                        mc.report(\"StreamCountFromCollection\", 0, expr, Roles.REPLACEMENT_METHOD.create(mr2\n                                .getDeclaringType().getInternalName(), \"size\", \"()I\"));\n                    }\n                }\n            }\n            if(isCollectionStream(mr)) {\n                Expression collection = Exprs.getChild(expr, 0);\n                if(!Inf.BACKLINK.findTransitiveUsages(collection, true).allMatch(Predicate.isEqual(expr))) {\n                    return;\n                }\n                if(collection.getCode() == AstCode.InvokeStatic) {\n                    MethodReference mr2 = (MethodReference)collection.getOperand();\n                    String replacement = null;\n                    if(Types.is(mr2.getDeclaringType(), Collections.class)) {\n                        if(mr2.getName().startsWith(\"singleton\"))\n                            replacement = \"Stream.of(\"+ExpressionFormatter.formatExpression(collection.getArguments().get(0))+\")\";\n                        else if(mr2.getName().startsWith(\"empty\"))\n                            replacement = \"Stream.empty()\";\n                    } else if(Types.is(mr2.getDeclaringType(), Arrays.class)) {\n                        if(mr2.getName().equals(\"asList\"))\n                            replacement = \"Stream.of(...)\";\n                    }\n                    if(replacement != null) {\n                        mc.report(\"RedundantCollectionStream\", 0, expr, Roles.REPLACEMENT_STRING.create(replacement));\n                    }\n                }\n            }\n            if(isOptionalIsPresent(mr)) {\n                Expression opt = expr.getArguments().get(0);\n                if(opt.getCode() == AstCode.Load && mc.isAnnotated()) {\n                    opt = Exprs.getChild(expr, 0);\n                    if(!Inf.BACKLINK.findTransitiveUsages(opt, true).allMatch(Predicate.isEqual(expr))) {\n                        return;\n                    }\n                }\n                if(opt.getCode() == AstCode.InvokeInterface || opt.getCode() == AstCode.InvokeVirtual) {\n                    MethodReference mr2 = (MethodReference) opt.getOperand();\n                    if(isStreamFind(mr2)) {\n                        Expression stream = Exprs.getChild(opt, 0);\n                        MethodReference mr3 = (MethodReference) stream.getOperand();\n                        if(isStreamFilter(mr3)) {\n                            mc.report(\"RedundantStreamFind\", 0, expr, Roles.REPLACEMENT_METHOD.create(mr3.getDeclaringType().getInternalName(), \"anyMatch\",\n                                \"(L\"+mr3.getParameters().get(0).getParameterType().getInternalName()+\";)Z\"));\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private boolean isCollectionStream(MethodReference mr) {\n        return mr.getName().equals(\"stream\") && mr.getParameters().isEmpty()\n            && Types.isCollection(mr.getDeclaringType());\n    }\n\n    private boolean isStreamForEach(MethodReference mr) {\n        return (mr.getName().equals(\"forEach\") || mr.getName().equals(\"forEachOrdered\"))\n                && mr.getErasedSignature().equals(\"(Ljava/util/function/Consumer;)V\")\n                && Types.isStream(mr.getDeclaringType());\n    }\n\n    private boolean isOptionalIsPresent(MethodReference mr) {\n        return mr.getName().equals(\"isPresent\") && mr.getDeclaringType().getInternalName().startsWith(\"java/util/Optional\");\n    }\n    \n    private boolean isStreamFind(MethodReference mr) {\n        return (mr.getName().equals(\"findFirst\") || mr.getName().equals(\"findAny\")) &&\n                mr.getErasedSignature().startsWith(\"()Ljava/util/Optional\") && Types.isBaseStream(mr.getDeclaringType());\n    }\n    \n    private boolean isStreamCount(MethodReference mr) {\n        return mr.getName().equals(\"count\") && Types.isBaseStream(mr.getDeclaringType());\n    }\n    \n    private boolean isStreamFilter(MethodReference mr) {\n        if(!mr.getName().equals(\"filter\") || mr.getParameters().size() != 1)\n            return false;\n        TypeReference type = mr.getParameters().get(0).getParameterType();\n        return type.getSimpleName().endsWith(\"Predicate\") && type.getPackageName().equals(\"java.util.function\")\n                && Types.isBaseStream(mr.getDeclaringType());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/RegexProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Arrays;\nimport java.util.Locale;\nimport java.util.regex.Pattern;\nimport java.util.regex.PatternSyntaxException;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Role.StringRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"RegexUnintended\", maxScore=85)\n@WarningDefinition(category=\"Correctness\", name=\"RegexFileSeparator\", maxScore=70)\n@WarningDefinition(category=\"Correctness\", name=\"RegexBadSyntax\", maxScore=80)\npublic class RegexProblems {\n    private static final StringRole ERROR_MESSAGE = StringRole.forName(\"ERROR_MESSAGE\");\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if(expr.getCode() == AstCode.InvokeStatic || expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            String type = mr.getDeclaringType().getInternalName();\n            String name = mr.getName();\n            String sig = mr.getSignature();\n            if (type.equals(\"java/util/regex/Pattern\") && (name.equals(\"compile\") && sig.equals(\"(Ljava/lang/String;)\") ||\n                    name.equals(\"matches\"))) {\n                checkRegexp(mc, Exprs.getChild(expr, 0), 0);\n            } else if(type.equals(\"java/util/regex/Pattern\") && name.equals(\"compile\") && sig.equals(\"(Ljava/lang/String;I)\")) {\n                Object flags = Nodes.getConstant(expr.getArguments().get(1));\n                checkRegexp(mc, Exprs.getChild(expr, 0), flags instanceof Integer ? (int)flags: 0);\n            } else if(type.equals(\"java/lang/String\") && (name.equals(\"replaceAll\") || name.equals(\"replaceFirst\")\n                    || name.equals(\"matches\") || name.equals(\"split\"))) {\n                checkRegexp(mc, Exprs.getChild(expr, 1), 0);\n                checkBadPatterns(mc, Exprs.getChild(expr, 1), name.equals(\"replaceAll\") ? Nodes.getConstant(expr.getArguments().get(2)) : null);\n            }\n        }\n    }\n\n    private void checkBadPatterns(MethodContext mc, Expression regexExpr, Object replacementObj) {\n        Object regexObj = Nodes.getConstant(regexExpr);\n        if(!(regexObj instanceof String)) {\n            return;\n        }\n        String regex = (String)regexObj;\n        if(regex.equals(\"|\")) {\n            mc.report(\"RegexUnintended\", 0, regexExpr, Roles.REGEXP.create(regex));\n        } else if(regex.equals(\".\")) {\n            if(replacementObj instanceof String) {\n                String replacement = (String) replacementObj;\n                if(Arrays.asList(\"x\", \"-\", \"*\", \" \", \"\\\\*\").contains(replacement.toLowerCase(Locale.ENGLISH)))\n                    return;\n            }\n            mc.report(\"RegexUnintended\", 10, regexExpr, Roles.REGEXP.create(regex));\n        }\n    }\n\n    private void checkRegexp(MethodContext mc, Expression regexExpr, int flags) {\n        if((flags & Pattern.LITERAL) == 0) {\n            if(regexExpr.getCode() == AstCode.GetStatic) {\n                FieldReference fr = (FieldReference) regexExpr.getOperand();\n                if(fr.getName().equals(\"separator\") && fr.getDeclaringType().getInternalName().equals(\"java/io/File\")) {\n                    mc.report(\"RegexFileSeparator\", 0, regexExpr);\n                }\n            } else if(regexExpr.getCode() == AstCode.InvokeVirtual) {\n                MethodReference mr = (MethodReference)regexExpr.getOperand();\n                if(mr.getName().equals(\"getSeparator\") && mr.getDeclaringType().getInternalName().equals(\"java/nio/file/FileSystem\")) {\n                    mc.report(\"RegexFileSeparator\", 0, regexExpr);\n                }\n            }\n        }\n        Object regexObj = Nodes.getConstant(regexExpr);\n        if(!(regexObj instanceof String)) {\n            return;\n        }\n        String regex = (String)regexObj;\n        try {\n            Pattern.compile(regex, flags);\n        } catch (PatternSyntaxException e) {\n            mc.report(\"RegexBadSyntax\", 0, regexExpr, Roles.REGEXP.create(regex), ERROR_MESSAGE.create(e.getMessage()));\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/ReturnNull.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Role.TypeRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"BadPractice\", name = \"OptionalReturnNull\", maxScore = 50)\n@WarningDefinition(category = \"BadPractice\", name = \"BooleanReturnNull\", maxScore = 50)\n@WarningDefinition(category = \"BadPractice\", name = \"ArrayReturnNull\", maxScore = 38)\npublic class ReturnNull {\n    private static final TypeRole RETURN_TYPE = TypeRole.forName(\"RETURN_TYPE\");\n\n    private static final Map<String, String> TYPE_TO_WARNING = new HashMap<>();\n\n    static {\n        TYPE_TO_WARNING.put(\"java/util/Optional\", \"OptionalReturnNull\");\n        TYPE_TO_WARNING.put(\"com/google/common/base/Optional\", \"OptionalReturnNull\");\n        TYPE_TO_WARNING.put(\"java/lang/Boolean\", \"BooleanReturnNull\");\n    }\n\n    public boolean checkMethod(MethodDefinition md) {\n        if(!md.getReturnType().isArray() && !TYPE_TO_WARNING.containsKey(md.getReturnType().getInternalName()))\n            return false;\n        if(md.getName().equals(\"getName\") && md.getReturnType().getSignature().equals(\"[B\") && md.getParameters().size() == 1\n                && Types.isInstance(md.getDeclaringType(), \"java/sql/ResultSet\"))\n            return false;\n        return true;\n    }\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc, MethodDefinition md, TypeDefinition td) {\n        if (expr.getCode() == AstCode.Return && !expr.getArguments().isEmpty()) {\n            Expression child = Exprs.getChild(expr, 0);\n            if (child.getCode() == AstCode.AConstNull) {\n                MethodDefinition curMethod = nc.getLambdaMethod();\n                if (curMethod == null)\n                    curMethod = md;\n                String warningType = curMethod.getReturnType().isArray() ? \"ArrayReturnNull\" : TYPE_TO_WARNING\n                        .get(curMethod.getReturnType().getInternalName());\n                if (warningType != null) {\n                    int priority = 0;\n                    if (!td.isPublic() || md.isPrivate() || md.isPackagePrivate())\n                        priority = 20;\n                    else if (md.isProtected() || md != curMethod)\n                        priority = 10;\n                    // Method which simply contains \"return null\": probably stub or something\n                    if(nc.getParent() == null && nc.isOnlyChild(expr))\n                        priority += 10;\n                    mc.report(warningType, priority, expr.getArguments().get(0), RETURN_TYPE.create(md\n                            .getReturnType()));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/RoughConstant.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.math.BigDecimal;\nimport java.math.MathContext;\nimport java.math.RoundingMode;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Warning;\n\n@WarningDefinition(category=\"BadPractice\", name=\"RoughConstantValue\", maxScore=60)\npublic class RoughConstant {\n    static class BadConstant {\n        String replacement;\n        double value;\n        int basePriority;\n\n        Set<Number> approxSet = new HashSet<>();\n\n        BadConstant(double base, double factor, String replacement, int basePriority) {\n            this.value = base * factor;\n            this.replacement = replacement;\n            this.basePriority = basePriority;\n            BigDecimal valueBig = BigDecimal.valueOf(value);\n            BigDecimal baseBig = BigDecimal.valueOf(base);\n            BigDecimal factorBig = BigDecimal.valueOf(factor);\n            for (int prec = 0; prec < 14; prec++) {\n                addApprox(baseBig.round(new MathContext(prec, RoundingMode.FLOOR)).multiply(factorBig));\n                addApprox(baseBig.round(new MathContext(prec, RoundingMode.CEILING)).multiply(factorBig));\n                addApprox(valueBig.round(new MathContext(prec, RoundingMode.FLOOR)));\n                addApprox(valueBig.round(new MathContext(prec, RoundingMode.CEILING)));\n            }\n        }\n\n        public boolean exact(Number candidate) {\n            if (candidate instanceof Double) {\n                return candidate.doubleValue() == value;\n            }\n            return candidate.floatValue() == (float) value;\n        }\n\n        public double diff(double candidate) {\n            return Math.abs(value - candidate) / value;\n        }\n\n        public boolean equalPrefix(Number candidate) {\n            return approxSet.contains(candidate);\n        }\n\n        private void addApprox(BigDecimal roundFloor) {\n            double approxDouble = roundFloor.doubleValue();\n            if (approxDouble != value && Math.abs(approxDouble - value) / value < 0.001) {\n                approxSet.add(approxDouble);\n            }\n            float approxFloat = roundFloor.floatValue();\n            if (Math.abs(approxFloat - value) / value < 0.001) {\n                approxSet.add(approxFloat);\n                approxSet.add((double) approxFloat);\n            }\n        }\n    }\n\n    private static final BadConstant[] badConstants = new BadConstant[] {\n        new BadConstant(Math.PI, 1, \"Math.PI\", 0),\n        new BadConstant(Math.PI, 1/2.0, \"Math.PI/2\", 10),\n        new BadConstant(Math.PI, 1/3.0, \"Math.PI/3\", 18),\n        new BadConstant(Math.PI, 1/4.0, \"Math.PI/4\", 16),\n        new BadConstant(Math.PI, 2, \"2*Math.PI\", 10),\n        new BadConstant(Math.E, 1, \"Math.E\", 17)\n    };\n\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext ctx, NodeChain parents) {\n        // Not use Nodes.getConstant here as direct usage should only be reported\n        if(expr.getCode() != AstCode.LdC)\n            return;\n        Object constant = expr.getOperand();\n        if(constant instanceof Float || constant instanceof Double) {\n            Number constValue = (Number)constant;\n            double candidate = constValue.doubleValue();\n            if (Double.isNaN(candidate) || Double.isInfinite(candidate)) {\n                return;\n            }\n            for (BadConstant badConstant : badConstants) {\n                int priority = getPriority(badConstant, constValue, candidate);\n                if(priority < Warning.MAX_SCORE) {\n                    Node parent = parents.getNode();\n                    if(Nodes.isBoxing(parent))\n                        parent = parents.getParent().getNode();\n                    if(Nodes.isOp(parent, AstCode.InitArray)) {\n                        int children = ((Expression)parent).getArguments().size();\n                        if(children > 100)\n                            priority += 30;\n                        else if(children > 10)\n                            priority += 20;\n                        else if(children > 5)\n                            priority += 10;\n                        else if(children > 1)\n                            priority += 5;\n                    }\n                    ctx.report(\"RoughConstantValue\", priority, expr, Roles.NUMBER.create(constValue),\n                        Roles.REPLACEMENT_STRING.create(badConstant.replacement));\n                }\n            }\n        }\n    }\n\n    private int getPriority(BadConstant badConstant, Number constValue, double candidate) {\n        if (badConstant.exact(constValue)) {\n            return Warning.MAX_SCORE;\n        }\n        double diff = badConstant.diff(candidate);\n        if (diff > 1e-3) {\n            return Warning.MAX_SCORE;\n        }\n        if (badConstant.equalPrefix(constValue)) {\n            return diff > 3e-4 ? badConstant.basePriority+25 :\n                diff > 1e-4 ? badConstant.basePriority+20 :\n                diff > 1e-5 ? badConstant.basePriority+15 :\n                diff > 1e-6 ? badConstant.basePriority+10 :\n                    badConstant.basePriority;\n        }\n        if (diff > 1e-7) {\n            return Warning.MAX_SCORE;\n        }\n        return badConstant.basePriority+20;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SameBranches.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.CaseBlock;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Switch;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Equi;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.LocationRole;\nimport one.util.huntbugs.warning.WarningAnnotation;\n\n/**\n * @author Tagir Valeev\n */\n@WarningDefinition(category = \"RedundantCode\", name = \"SameBranchesIf\", maxScore = 70)\n@WarningDefinition(category = \"RedundantCode\", name = \"SameBranchesTernary\", maxScore = 70)\n@WarningDefinition(category = \"RedundantCode\", name = \"SameBranchesSwitch\", maxScore = 70)\n@WarningDefinition(category = \"RedundantCode\", name = \"SameBranchesSwitchDefault\", maxScore = 70)\n@WarningDefinition(category = \"RedundantCode\", name = \"EmptyBranch\", maxScore = 25)\npublic class SameBranches {\n    private static final LocationRole SAME_BRANCH = LocationRole.forName(\"SAME_BRANCH\");\n    private static final LocationRole DEFAULT_BRANCH = LocationRole.forName(\"DEFAULT_BRANCH\");\n    \n    @MethodVisitor\n    public boolean check(MethodDefinition md, TypeDefinition td) {\n        // Skip some autogenerated methods\n        if(md.getName().equals(\"yyparse\") && md.getReturnType().getInternalName().equals(\"java/lang/Object\"))\n            return false;\n        if(md.getName().startsWith(\"jj\") && td.getName().endsWith(\"TokenManager\"))\n            return false;\n        if(md.getName().equals(\"getNextToken\") && md.getSignature().equals(\"()I\"))\n            return false;\n        return true;\n    }\n    \n    @AstVisitor\n    public void visit(Node node, NodeChain nc, MethodContext mc) {\n        if (node instanceof Condition) {\n            Condition cond = (Condition) node;\n            if (Equi.equiBlocks(cond.getTrueBlock(), cond.getFalseBlock())) {\n                if (cond.getTrueBlock() == null || cond.getTrueBlock().getBody().isEmpty()) {\n                    mc.report(\"EmptyBranch\", 0, cond.getCondition());\n\n                } else {\n                    // Work-around procyon bug in string switch reconstruction\n                    if(cond.getTrueBlock().getBody().size() == 1 && Nodes.isOp(cond.getTrueBlock().getBody().get(0), AstCode.LoopOrSwitchBreak)\n                            && nc.getNode() instanceof CaseBlock)\n                        return;\n                    mc.report(\"SameBranchesIf\", computePriority(cond.getTrueBlock(), 2), cond.getTrueBlock(),\n                        SAME_BRANCH.create(mc, cond.getFalseBlock()));\n                }\n            }\n        }\n        if (node instanceof Switch) {\n            Switch sw = (Switch) node;\n            List<CaseBlock> blocks = sw.getCaseBlocks().stream().filter(\n                cb -> nonFallThrough(cb.getBody()) && !Nodes.isEmptyOrBreak(cb)).collect(Collectors.toList());\n            BitSet marked = new BitSet();\n            boolean hasDefault = false;\n            List<WarningAnnotation<?>> eqLocations = new ArrayList<>();\n            for (int i = 0; i < blocks.size(); i++) {\n                if (marked.get(i))\n                    continue;\n                for (int j = i + 1; j < blocks.size(); j++) {\n                    if (Equi.equiBlocks(blocks.get(i), blocks.get(j))) {\n                        marked.set(j);\n                        LocationRole role = SAME_BRANCH;\n                        if(blocks.get(j).isDefault()) {\n                            role = DEFAULT_BRANCH;\n                            hasDefault = true;\n                        }\n                        eqLocations.add(role.create(mc, blocks.get(j)));\n                    }\n                }\n                if (!eqLocations.isEmpty()) {\n                    int n = eqLocations.size() + (hasDefault ? 0 : 1);\n                    if (n > 3)\n                        n = (n - 3) / 2 + 3;\n                    CaseBlock block = blocks.get(i);\n                    mc.report(hasDefault ? \"SameBranchesSwitchDefault\" : \"SameBranchesSwitch\", computePriority(block, n), block,\n                        eqLocations);\n                    eqLocations.clear();\n                    hasDefault = false;\n                }\n            }\n        }\n    }\n\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visitExpr(Expression expr, MethodContext mc) {\n        if (expr.getCode() == AstCode.TernaryOp\n            && Equi.equiExpressions(expr.getArguments().get(1), expr.getArguments().get(2))) {\n            mc.report(\"SameBranchesTernary\", computePriority(expr.getArguments().get(1), 30), expr);\n        }\n    }\n\n    private static int computePriority(Node block, int n) {\n        int codeSize = Nodes.estimateCodeSize(block);\n        return Math.max(0, 55 - ((int) (Math.sqrt(n * codeSize) * 5)));\n    }\n\n    // This check is not complete: it's still possible that it will return false for nonFallThrough\n    private static boolean nonFallThrough(List<Node> body) {\n        if (body.isEmpty())\n            return false;\n        Node last = body.get(body.size() - 1);\n        return Nodes.isOp(last, AstCode.LoopOrSwitchBreak) || Nodes.isOp(last, AstCode.Return)\n            || Nodes.isOp(last, AstCode.LoopContinue) || Nodes.isOp(last, AstCode.AThrow);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SameIfChain.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Equi;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Role.ExpressionRole;\nimport one.util.huntbugs.warning.Role.LocationRole;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category = \"RedundantCode\", name = \"SameConditionChain\", maxScore = 50)\npublic class SameIfChain {\n    private static final LocationRole SAME_CONDITION_AT = LocationRole.forName(\"SAME_CONDITION_AT\");\n    private static final ExpressionRole SAME_EXPRESSION = ExpressionRole.forName(\"SAME_EXPRESSION\");\n    \n    @AstVisitor\n    public boolean visit(Node node, MethodContext mc, MethodDefinition md, TypeDefinition td) {\n        if (!mc.isAnnotated())\n            return false;\n        if (node instanceof Block) {\n            List<Node> body = ((Block) node).getBody();\n            if(!body.isEmpty()) {\n                Node second = body.get(0);\n                for (int i = 1; i < body.size(); i++) {\n                    Node first = second;\n                    second = body.get(i);\n                    if (first instanceof Condition && second instanceof Condition) {\n                        Condition cond1 = (Condition) first;\n                        Condition cond2 = (Condition) second;\n                        if (!cond1.getFalseBlock().getBody().isEmpty())\n                            continue;\n                        Expression c1 = cond1.getCondition();\n                        Expression c2 = cond2.getCondition();\n                        if (Nodes.isPure(c1) && Equi.equiExpressions(c1, c2)) {\n                            Set<Variable> vars = Exprs.stream(c1).filter(e -> e.getCode() == AstCode.Load).map(\n                                e -> (Variable) e.getOperand()).collect(Collectors.toSet());\n                            if (Nodes.find(cond1.getTrueBlock(), n -> Nodes.isWriteTo(n, vars)) != null)\n                                continue;\n                            // autogenerated by javacc\n                            if (md.getName().startsWith(\"jj\") && td.getSimpleName().endsWith(\"TokenManager\"))\n                                return false;\n                            int priority = 0;\n                            if(!cond1.getTrueBlock().getBody().isEmpty() &&\n                                    cond1.getTrueBlock().getBody().get(0) instanceof Condition) {\n                                priority += 15;\n                            }\n                            if(!cond2.getFalseBlock().getBody().isEmpty()) {\n                                priority += 10;\n                            }\n                            mc.report(\"SameConditionChain\", priority, c1, SAME_CONDITION_AT.create(mc, c2),\n                                Roles.EXPRESSION.create(c1), SAME_EXPRESSION.create(c2));\n                        }\n                    }\n                }\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SelfAssignment.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"SelfAssignmentField\", maxScore=80)\n@WarningDefinition(category=\"Correctness\", name=\"SelfAssignmentLocal\", maxScore=80)\n@WarningDefinition(category=\"Correctness\", name=\"SelfAssignmentLocalInsteadOfField\", maxScore=90)\n@WarningDefinition(category=\"Correctness\", name=\"SelfAssignmentArrayElement\", maxScore=80)\npublic class SelfAssignment {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc, MethodDefinition md, TypeDefinition td) {\n        if(expr.getCode() == AstCode.PutField) {\n            FieldDefinition frPut = ((FieldReference) expr.getOperand()).resolve();\n            if(frPut != null) {\n                Expression getField = Exprs.getChild(expr, 1);\n                if(getField.getCode() == AstCode.GetField) {\n                    FieldReference frGet = (FieldReference) getField.getOperand();\n                    if(frPut.equals(frGet.resolve())) {\n                        Node selfPut = Exprs.getChildNoSpecial(expr, 0);\n                        Node selfGet = Exprs.getChildNoSpecial(getField, 0);\n                        if(Nodes.isEquivalent(selfGet, selfPut)) {\n                            mc.report(\"SelfAssignmentField\", 0, expr);\n                        }\n                    }\n                }\n            }\n        } else if(expr.getCode() == AstCode.PutStatic) {\n            FieldDefinition frPut = ((FieldReference) expr.getOperand()).resolve();\n            if(frPut != null) {\n                Expression getStatic = Exprs.getChild(expr, 0);\n                if(getStatic.getCode() == AstCode.GetStatic) {\n                    FieldReference frGet = (FieldReference) getStatic.getOperand();\n                    if(frPut.equals(frGet.resolve())) {\n                        mc.report(\"SelfAssignmentField\", 0, expr);\n                    }\n                }\n            }\n        } else if(expr.getCode() == AstCode.StoreElement) {\n            Expression sourceRef = Exprs.getChild(expr, 2);\n            if(sourceRef.getCode() == AstCode.LoadElement) {\n                Expression storeArrayRef = Exprs.getChildNoSpecial(expr, 0);\n                Expression storeIndexRef = Exprs.getChildNoSpecial(expr, 1);\n                Expression loadArrayRef = Exprs.getChildNoSpecial(sourceRef, 0);\n                Expression loadIndexRef = Exprs.getChildNoSpecial(sourceRef, 1);\n                if(Nodes.isEquivalent(storeArrayRef, loadArrayRef) && Nodes.isEquivalent(storeIndexRef, loadIndexRef)) {\n                    mc.report(\"SelfAssignmentArrayElement\", 0, expr);\n                }\n            }\n        } else if(expr.getCode() == AstCode.Store) {\n            Expression ref = expr.getArguments().get(0);\n            if(ref.getCode() == AstCode.Load && ref.getOperand() == expr.getOperand()) {\n                if(!md.isStatic()) {\n                    Variable v = (Variable)ref.getOperand();\n                    for(FieldDefinition fd : td.getDeclaredFields()) {\n                        if(fd.getName().equals(v.getName())) {\n                            mc.report(\"SelfAssignmentLocalInsteadOfField\", 0, expr, Roles.FIELD.create(fd));\n                            return;\n                        }\n                    }\n                }\n                mc.report(\"SelfAssignmentLocal\", 0, expr);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SelfComputation.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"SelfComputation\", maxScore = 70)\n@WarningDefinition(category = \"Correctness\", name = \"SelfComparison\", maxScore = 70)\n@WarningDefinition(category = \"Correctness\", name = \"SelfEquals\", maxScore = 70)\npublic class SelfComputation {\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if ((expr.getCode() == AstCode.And || expr.getCode() == AstCode.Or || expr.getCode() == AstCode.Xor\n            || expr.getCode() == AstCode.Sub || expr.getCode() == AstCode.Div || expr.getCode() == AstCode.Rem)\n            && sameArgs(expr)) {\n            mc.report(\"SelfComputation\", 0, expr.getArguments().get(0), Roles.OPERATION.create(expr));\n        } else if (expr.getCode().isComparison() && sameArgs(expr)) {\n            JvmType type = expr.getArguments().get(0).getInferredType().getSimpleType();\n            if ((expr.getCode() != AstCode.CmpEq && expr.getCode() != AstCode.CmpNe)\n                || (type != JvmType.Double && type != JvmType.Float))\n                mc.report(\"SelfComparison\", 0, expr.getArguments().get(0), Roles.OPERATION.create(expr.getCode()));\n        } else if (expr.getCode() == AstCode.InvokeVirtual\n            && Methods.isEqualsMethod((MethodReference) expr.getOperand()) && sameArgs(expr) &&\n            !Exprs.isAssertion(expr)) {\n            mc.report(\"SelfEquals\", 0, expr.getArguments().get(0));\n        }\n    }\n\n    private boolean sameArgs(Expression expr) {\n        return expr.getArguments().size() == 2\n            && Nodes.isEquivalent(expr.getArguments().get(0), expr.getArguments().get(1));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SerializationIdiom.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.registry.ClassContext;\nimport one.util.huntbugs.registry.FieldContext;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.FieldVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Role.TypeRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Serialization\", name=\"ComparatorIsNotSerializable\", maxScore=45)\n@WarningDefinition(category=\"Serialization\", name=\"SerialVersionUidNotFinal\", maxScore=50)\n@WarningDefinition(category=\"Serialization\", name=\"SerialVersionUidNotStatic\", maxScore=50)\n@WarningDefinition(category=\"Serialization\", name=\"SerialVersionUidNotLong\", maxScore=40)\n@WarningDefinition(category=\"Serialization\", name=\"SerializationMethodMustBePrivate\", maxScore=60)\n@WarningDefinition(category=\"Serialization\", name=\"ReadResolveMustReturnObject\", maxScore=60)\n@WarningDefinition(category=\"Serialization\", name=\"ReadResolveIsStatic\", maxScore=60)\n@WarningDefinition(category=\"Serialization\", name=\"ReadObjectIsSynchronized\", maxScore=60)\n@WarningDefinition(category=\"Serialization\", name=\"WriteObjectIsSynchronized\", maxScore=40)\npublic class SerializationIdiom {\n    private static final TypeRole SHOULD_IMPLEMENT = TypeRole.forName(\"SHOULD_IMPLEMENT\");\n    \n    boolean isSerializable;\n    \n    @ClassVisitor\n    public void visitClass(TypeDefinition td, ClassContext cc) {\n        isSerializable = Types.isInstance(td, \"java/io/Serializable\");\n        if(Types.isInstance(td, \"java/util/Comparator\") && !td.isAnonymous() && !td.isLocalClass()\n                && !isSerializable) {\n            int priority = 0;\n            for(FieldDefinition fd : td.getDeclaredFields()) {\n                TypeReference fieldType = fd.getFieldType();\n                while(fieldType.isArray())\n                    fieldType = fieldType.getElementType();\n                if(fieldType.isPrimitive())\n                    continue;\n                if(Types.isInstance(fieldType, \"java/io/Serializable\")) {\n                    priority+=10;\n                    if(priority > 20)\n                        break;\n                }\n            }\n            cc.report(\"ComparatorIsNotSerializable\", priority, SHOULD_IMPLEMENT.create(\"java/io/Serializable\"));\n        }\n    }\n    \n    @MethodVisitor\n    public void visitMethod(MethodDefinition md, MethodContext mc, TypeDefinition td) {\n        if(isSerializable) {\n            switch(md.getName()) {\n            case \"readResolve\":\n                if(md.getSignature().startsWith(\"()\")) {\n                    if(!md.getSignature().equals(\"()Ljava/lang/Object;\")) {\n                        mc.report(\"ReadResolveMustReturnObject\", 0);\n                    } else if(md.isStatic()) {\n                        mc.report(\"ReadResolveIsStatic\", 0);\n                    }\n                }\n                break;\n            case \"readObject\":\n                if(md.getSignature().equals(\"(Ljava/io/ObjectInputStream;)V\")) {\n                    if(!md.isPrivate())\n                        mc.report(\"SerializationMethodMustBePrivate\", 0);\n                    if(Flags.testAny(md.getFlags(), Flags.SYNCHRONIZED))\n                        mc.report(\"ReadObjectIsSynchronized\", 0);\n                }\n                break;\n            case \"readObjectNoData\":\n                if(md.getSignature().equals(\"()V\") && !md.isPrivate())\n                    mc.report(\"SerializationMethodMustBePrivate\", 0);\n                break;\n            case \"writeObject\":\n                if(md.getSignature().equals(\"(Ljava/io/ObjectOutputStream;)V\")) {\n                    if(!md.isPrivate())\n                        mc.report(\"SerializationMethodMustBePrivate\", 0);\n                    if(Flags.testAny(md.getFlags(), Flags.SYNCHRONIZED) &&\n                            td.getDeclaredMethods().stream().noneMatch(m -> m != md &&\n                                    Flags.testAny(m.getFlags(), Flags.SYNCHRONIZED)))\n                        mc.report(\"WriteObjectIsSynchronized\", 0);\n                }\n                break;\n            }\n        }\n    }\n    \n    @FieldVisitor\n    public void visitField(FieldDefinition fd, FieldContext fc) {\n        if(fd.getName().equals(\"serialVersionUID\")) {\n            if(!fd.isFinal()) {\n                fc.report(\"SerialVersionUidNotFinal\", 0);\n            }\n            if(!fd.isStatic()) {\n                fc.report(\"SerialVersionUidNotStatic\", 0);\n            }\n            if(fd.getFieldType().getSimpleType() == JvmType.Integer) {\n                fc.report(\"SerialVersionUidNotLong\", 0);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SpinLoop.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Loop;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Multithreading\", name = \"SpinLoopOnField\", maxScore = 70)\npublic class SpinLoop {\n    @AstVisitor\n    public void visit(Node node, MethodContext mc) {\n        if (!(node instanceof Loop))\n            return;\n        Loop loop = (Loop) node;\n        if (!loop.getBody().getBody().isEmpty())\n            return;\n        Expression cond = loop.getCondition();\n        if (cond == null || !Nodes.isSideEffectFree(cond))\n            return;\n        checkCondition(cond, mc);\n    }\n\n    private void checkCondition(Expression cond, MethodContext mc) {\n        if (cond.getCode() == AstCode.LogicalAnd || cond.getCode() == AstCode.LogicalOr\n            || cond.getCode() == AstCode.LogicalNot) {\n            cond.getArguments().forEach(child -> checkCondition(child, mc));\n        } else if (cond.getCode().isComparison()) {\n            cond.getArguments().forEach(child -> checkFieldRead(child, mc));\n        } else\n            checkFieldRead(cond, mc);\n    }\n\n    private void checkFieldRead(Expression expr, MethodContext mc) {\n        if (Nodes.isFieldRead(expr)) {\n            FieldDefinition fd = ((FieldReference) expr.getOperand()).resolve();\n            if (fd != null && !Flags.testAny(fd.getFlags(), Flags.VOLATILE)) {\n                mc.report(\"SpinLoopOnField\", 0, expr, Roles.FIELD.create(fd));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SqlBadArgument.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"Correctness\", name=\"BadResultSetArgument\", maxScore=75)\n@WarningDefinition(category=\"Correctness\", name=\"BadPreparedStatementArgument\", maxScore=75)\npublic class SqlBadArgument {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if(expr.getCode() == AstCode.InvokeInterface) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            String warningType = null;\n            if (mr.getDeclaringType().getInternalName().equals(\"java/sql/ResultSet\")\n                && (mr.getName().startsWith(\"get\") || mr.getName().startsWith(\"update\"))\n                && firstParameterIsInt(mr)) {\n                warningType = \"BadResultSetArgument\";\n            } else if(mr.getDeclaringType().getInternalName().equals(\"java/sql/PreparedStatement\") &&\n                    mr.getName().startsWith(\"set\") && firstParameterIsInt(mr)) {\n                warningType = \"BadPreparedStatementArgument\";\n            }\n            if(warningType != null) {\n                Expression arg = expr.getArguments().get(1);\n                Object constant = Nodes.getConstant(arg);\n                if (Integer.valueOf(0).equals(constant)) {\n                    mc.report(warningType, 0, expr);\n                } else if (ValuesFlow.reduce(arg, e -> Integer.valueOf(0).equals(Nodes.getConstant(e)),\n                    Boolean::logicalOr, Boolean::booleanValue)) {\n                    mc.report(warningType, 20, expr);\n                }\n            }\n        }\n    }\n    \n    private boolean firstParameterIsInt(MethodReference mr) {\n        return mr.getParameters().size() > 0\n            && mr.getParameters().get(0).getParameterType().getSimpleType() == JvmType.Integer;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/StartInConstructor.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.db.Hierarchy.TypeHierarchy;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Multithreading\", name = \"StartInConstructor\", maxScore = 50)\npublic class StartInConstructor {\n    @MethodVisitor\n    public boolean checkMethod(MethodDefinition md, TypeDefinition td) {\n        return td.isPublic() && !td.isFinal() && !md.isPrivate() && !md.isPackagePrivate();\n    }\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS, methodName = \"<init>\")\n    public boolean visit(Expression expr, NodeChain nc, MethodContext mc, TypeHierarchy th) {\n        if (expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if (mr.getName().equals(\"start\") && mr.getSignature().equals(\"()V\")) {\n                if (Types.isInstance(mr.getDeclaringType(), \"java/lang/Thread\")) {\n                    int priority = 0;\n                    if (th != null) {\n                        if (!th.hasSubClasses())\n                            priority += 10;\n                        else if (!th.hasSubClassesOutOfPackage())\n                            priority += 5;\n                    }\n                    List<Node> body = nc.getRoot().getBody();\n                    if (body.get(body.size() - 1) == expr)\n                        priority += 10;\n                    mc.report(\"StartInConstructor\", priority, expr);\n                }\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/StaticFieldFromInstanceMethod.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Locale;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"BadPractice\", name=\"StaticFieldFromInstanceMethod\", maxScore=55)\npublic class StaticFieldFromInstanceMethod {\n    @MethodVisitor\n    public boolean check(MethodDefinition md) {\n        return !md.isStatic();\n    }\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc, MethodDefinition md, TypeDefinition td) {\n        if(expr.getCode() == AstCode.PutStatic) {\n            FieldReference fr = (FieldReference) expr.getOperand();\n            FieldDefinition fd = fr.resolve();\n            if(fd != null && fd.isSynthetic())\n                return;\n            int priority = 0;\n            if(md.isPrivate() || td.isPrivate())\n                priority += 20;\n            else if(!md.isPublic() || !td.isPublic())\n                priority += 10;\n            else if(md.isConstructor())\n                priority += 5;\n            if(nc.isSynchronized() || Flags.testAny(md.getFlags(), Flags.SYNCHRONIZED))\n                priority += 15;\n            if(Exprs.getChild(expr, 0).getCode() == AstCode.AConstNull)\n                priority += 5;\n            \n            String name = fr.getName().toLowerCase(Locale.ENGLISH);\n            if (fr.getFieldType().getSimpleType() == JvmType.Boolean) {\n                priority += 10;\n                if (name.contains(\"verbose\"))\n                    priority += 5;\n            }\n            if (name.contains(\"debug\"))\n                priority += 15;\n            if ((md.getName().equals(\"start\") || md.getName().equals(\"stop\")) && md.getErasedSignature().equals(\n                \"(Lorg/osgi/framework/BundleContext;)V\") && Types.isInstance(td,\n                    \"org/osgi/framework/BundleActivator\")) {\n                priority += 30;\n            }\n            mc.report(\"StaticFieldFromInstanceMethod\", priority, expr);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/StaticFieldNonThreadSafe.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.FieldContext;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.FieldVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.AccessLevel;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author shustkost\n *\n */\n@WarningDefinition(category=\"Multithreading\", name=\"StaticNotThreadSafeField\", maxScore=60)\n@WarningDefinition(category=\"Multithreading\", name=\"StaticNotThreadSafeFieldInvoke\", maxScore=60)\npublic class StaticFieldNonThreadSafe {\n    private static final Set<String> DANGEROUS_METHODS = new HashSet<>(Arrays.asList(\"format\", \"add\", \"clear\", \"parse\", \"applyPattern\"));\n    \n    @FieldVisitor\n    public void visitField(FieldDefinition fd, FieldContext fc, TypeDefinition td) {\n        if((fd.isPublic() || fd.isProtected()) && (td.isPublic() || td.isProtected()) &&\n                fd.isStatic() && !fd.isEnumConstant()) {\n            TypeReference fieldType = fd.getFieldType();\n            if(!isNotThreadSafe(fieldType))\n                return;\n            fc.report(\"StaticNotThreadSafeField\", AccessLevel.of(fd).select(0, 20, 100, 100),\n                Roles.FIELD_TYPE.create(fieldType));\n        }\n    }\n\n    /**\n     * @param fieldType\n     * @return\n     */\n    private boolean isNotThreadSafe(TypeReference fieldType) {\n        return Types.isInstance(fieldType, \"java/util/Calendar\") || Types.isInstance(fieldType, \"java/text/DateFormat\");\n    }\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visitCall(MethodContext mc, Expression expr, MethodDefinition md, NodeChain nc, TypeDefinition td) {\n        if(expr.getCode() == AstCode.InvokeVirtual) {\n            Expression target = expr.getArguments().get(0);\n            if(target.getCode() != AstCode.GetStatic) {\n                target = Exprs.getChild(expr, 0);\n            }\n            if(target.getCode() == AstCode.GetStatic) {\n                FieldReference fr = (FieldReference) target.getOperand();\n                if(md.isTypeInitializer() && td.isEquivalentTo(fr.getDeclaringType()))\n                    return;\n                MethodReference mr = (MethodReference) expr.getOperand();\n                String methodName = mr.getName();\n                if(methodName.startsWith(\"get\"))\n                    return;\n                if(nc.isSynchronized() || Flags.testAny(md.getFlags(), Flags.SYNCHRONIZED))\n                    return;\n                if(!isNotThreadSafe(mr.getDeclaringType()))\n                    return;\n                int priority = 0;\n                if(Methods.isMain(md))\n                    priority += 30;\n                if(!methodName.startsWith(\"set\") && !DANGEROUS_METHODS.contains(methodName))\n                    priority += 20;\n                mc.report(\"StaticNotThreadSafeFieldInvoke\", priority, expr, Roles.FIELD.create(fr));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/StringConcatInLoop.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Loop;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Switch;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Performance\", name = \"StringConcatInLoop\", maxScore = 50)\npublic class StringConcatInLoop {\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression store, NodeChain nc, MethodContext mc, MethodDefinition md, TypeDefinition td) {\n        if (store.getCode() != AstCode.Store)\n            return;\n        Variable var = (Variable) store.getOperand();\n        Expression stringExpr = store.getArguments().get(0);\n        if (!isStringBuilderToString(stringExpr))\n            return;\n        Expression appendSource = getAppendSource(stringExpr);\n        if (appendSource == null)\n            return;\n        if (appendSource.getCode() != AstCode.Load)\n            return;\n        Variable loadVar = (Variable) appendSource.getOperand();\n        if (loadVar != var)\n            return;\n        // String concatenation like a += x+y+z found\n        if (!ValuesFlow.hasPhiSource(appendSource))\n            return;\n        List<Expression> phiArgs = new ArrayList<>(ValuesFlow.getSource(appendSource).getArguments());\n        phiArgs.remove(stringExpr);\n        NodeChain parent = nc;\n        int priority = 0;\n        while (parent != null) {\n            Node node = parent.getNode();\n            if (node instanceof Condition || node instanceof Switch)\n                priority += 5;\n            if (node instanceof Loop && !phiArgs.stream().allMatch(src -> Nodes.find(node, src::equals) != null)) {\n                // Autogenerated by javacc\n                if (md.getName().equals(\"initialise\") && md.isStatic() && md.isPrivate() &&\n                        Types.isString(md.getReturnType())\n                    && td.getSimpleName().equals(\"ParseException\"))\n                    return;\n                mc.report(\"StringConcatInLoop\", priority, store);\n                return;\n            }\n            parent = parent.getParent();\n        }\n    }\n\n    private boolean isStringValueOf(Expression expr) {\n        if (expr.getCode() != AstCode.InvokeStatic)\n            return false;\n        MethodReference mr = (MethodReference) expr.getOperand();\n        return mr.getName().equals(\"valueOf\") && mr.getSignature().equals(\"(Ljava/lang/Object;)Ljava/lang/String;\")\n            && mr.getDeclaringType().getInternalName().equals(\"java/lang/String\");\n    }\n\n    private boolean isStringBuilderToString(Expression expr) {\n        if (expr.getCode() != AstCode.InvokeVirtual)\n            return false;\n        MethodReference mr = (MethodReference) expr.getOperand();\n        return mr.getName().equals(\"toString\") && mr.getSignature().equals(\"()Ljava/lang/String;\")\n            && mr.getDeclaringType().getInternalName().startsWith(\"java/lang/StringBu\");\n    }\n\n    private Expression getAppendSource(Expression expr) {\n        Expression arg = expr.getArguments().get(0);\n        Expression prev = null;\n        while (true) {\n            if (arg.getCode() == AstCode.InitObject) {\n                if(prev == null)\n                    return null;\n                if(arg.getArguments().isEmpty()) {\n                    // javac scenario\n                    return prev.getArguments().size() == 2 ? prev.getArguments().get(1) : null;\n                }\n                if(arg.getArguments().size() == 1) {\n                    // ecj scenario\n                    Expression ctorArg = arg.getArguments().get(0);\n                    while (isStringValueOf(ctorArg))\n                        ctorArg = ctorArg.getArguments().get(0);\n                    return ctorArg;\n                }\n                return null;\n            }\n            if (arg.getCode() != AstCode.InvokeVirtual)\n                return null;\n            MethodReference appendMr = (MethodReference) arg.getOperand();\n            if (!appendMr.getName().equals(\"append\")\n                || !appendMr.getDeclaringType().getInternalName().startsWith(\"java/lang/StringBu\"))\n                return null;\n            prev = arg;\n            arg = arg.getArguments().get(0);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/StringIndex.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Role.NumberRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"RedundantCode\", name = \"UselessStringSubstring\", maxScore = 50)\n@WarningDefinition(category = \"RedundantCode\", name = \"StringIndexIsLessThanZero\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"StringIndexIsGreaterThanAllowed\", maxScore = 60)\npublic class StringIndex {\n    private static final NumberRole INDEX = NumberRole.forName(\"INDEX\");\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression node, MethodContext ctx) {\n        if (Nodes.isInvoke(node) && node.getCode() != AstCode.InvokeDynamic) {\n            check(node, (MethodReference) node.getOperand(), ctx);\n        }\n    }\n\n    private void check(Expression node, MethodReference mr, MethodContext mc) {\n        String typeName = mr.getDeclaringType().getInternalName();\n        String name = mr.getName();\n        String signature = mr.getSignature();\n        boolean isString = typeName.equals(\"java/lang/String\");\n        if (isString) {\n            Object val = Nodes.getConstant(node.getArguments().get(0));\n            String str = val instanceof String ? (String) val : null;\n            int strLen = str == null ? Integer.MAX_VALUE : str.length();\n            if (name.equals(\"substring\") || name.equals(\"subSequence\")) {\n                Object idxObj = Nodes.getConstant(node.getArguments().get(1));\n                boolean twoArg = signature.startsWith(\"(II)\");\n                int len = strLen;\n                if (twoArg) {\n                    Object lenObj = Nodes.getConstant(node.getArguments().get(2));\n                    if (lenObj instanceof Integer) {\n                        len = (int) lenObj;\n                        checkRange(node, strLen, len, mc);\n                    }\n                }\n                if (idxObj instanceof Integer) {\n                    int idx = (int) idxObj;\n                    if (idx == 0 && !twoArg) {\n                        mc.report(\"UselessStringSubstring\", 0, node);\n                    } else {\n                        checkRange(node, len, idx, mc);\n                    }\n                }\n            } else if ((name.equals(\"charAt\") || name.equals(\"codePointAt\")) && signature.startsWith(\"(I)\")) {\n                Object idx = Nodes.getConstant(node.getArguments().get(1));\n                if (idx instanceof Integer) {\n                    int i = (int) idx;\n                    checkRange(node, strLen - 1, i, mc);\n                }\n            }\n        }\n    }\n\n    private void checkRange(Expression expr, int maxValue, int val, MethodContext mc) {\n        if (val < 0) {\n            mc.report(\"StringIndexIsLessThanZero\", 0, expr, INDEX.create(val));\n        } else if (val > maxValue) {\n            mc.report(\"StringIndexIsGreaterThanAllowed\", 0, expr, INDEX.create(val), Roles.MAX_VALUE.create(maxValue));\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/StringUsage.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Performance\", name=\"StringConstructor\", maxScore=50)\n@WarningDefinition(category=\"Performance\", name=\"StringConstructorEmpty\", maxScore=55)\n@WarningDefinition(category=\"RedundantCode\", name=\"StringToString\", maxScore=40)\npublic class StringUsage {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression node, MethodContext mc, MethodDefinition md) {\n        if(node.getCode() == AstCode.InitObject) {\n            MethodReference mr = (MethodReference) node.getOperand();\n            if(mr.getDeclaringType().getInternalName().equals(\"java/lang/String\")) {\n                int priority = 0;\n                if(md.isTypeInitializer()) {\n                    // Static field initializer: only one object is created\n                    // not a big performance problem and probably intended\n                    Set<Expression> usages = Inf.BACKLINK.findUsages(node);\n                    if(usages.size() == 1 && usages.iterator().next().getCode() == AstCode.PutStatic) {\n                        priority = 15;\n                        node = usages.iterator().next();\n                    }\n                }\n                if(mr.getSignature().equals(\"()V\")) {\n                    mc.report(\"StringConstructorEmpty\", priority, node);\n                } else if(mr.getSignature().equals(\"(Ljava/lang/String;)V\")) {\n                    mc.report(\"StringConstructor\", priority, node);\n                }\n            }\n        } else if(node.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) node.getOperand();\n            if(mr.getDeclaringType().getInternalName().equals(\"java/lang/String\") && mr.getName().equals(\"toString\")) {\n                mc.report(\"StringToString\", 0, node.getArguments().get(0), Roles.CALLED_METHOD.create(mr));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SwingProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.NodeChain;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"BadPractice\", name=\"SwingMethodNotInSwingThread\", maxScore=40)\npublic class SwingProblems {\n    @MethodVisitor\n    public boolean check(MethodDefinition md) {\n        return Methods.isMain(md);\n    }\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc) {\n        if(expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if(mr.getDeclaringType().getInternalName().startsWith(\"javax/swing/\") &&\n                    (mr.getName().equals(\"show\") && mr.getSignature().equals(\"()V\") ||\n                            mr.getName().equals(\"pack\") && mr.getSignature().equals(\"()V\") ||\n                            mr.getName().equals(\"setVisible\") && mr.getSignature().equals(\"(Z)V\"))) {\n                if(nc.getLambdaMethod() != null) {\n                    return;\n                }\n                mc.report(\"SwingMethodNotInSwingThread\", 0, expr);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SyncGetClass.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.TryCatchBlock;\n\nimport one.util.huntbugs.db.Hierarchy.TypeHierarchy;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"Multithreading\", name=\"SyncOnGetClass\", maxScore=65)\npublic class SyncGetClass {\n    @MethodVisitor\n    public boolean checkMethod(MethodDefinition md, TypeDefinition td) {\n        return !td.isFinal() && !md.isStatic();\n    }\n    \n    @AstVisitor\n    public void visit(Node node, MethodContext mc, TypeHierarchy th, TypeDefinition td) {\n        if(node instanceof TryCatchBlock) {\n            Expression syncObject = Nodes.getSyncObject((TryCatchBlock) node);\n            if(syncObject != null) {\n                if(syncObject.getCode() == AstCode.InvokeVirtual && Methods.isGetClass((MethodReference) syncObject.getOperand())\n                        && Exprs.isThis(Exprs.getChild(syncObject, 0))) {\n                    int priority = 0;\n                    if(th != null && !th.hasSubClasses())\n                        priority += 10;\n                    if(Nodes.find(node, n -> isStaticFieldAccess(n, td)) == null)\n                        priority += 15;\n                    mc.report(\"SyncOnGetClass\", priority, syncObject);\n                }\n            }\n        }\n    }\n\n    private boolean isStaticFieldAccess(Node n, TypeDefinition td) {\n        if(!Nodes.isOp(n, AstCode.GetStatic) && !Nodes.isOp(n, AstCode.PutStatic))\n            return false;\n        FieldReference fr = (FieldReference)((Expression)n).getOperand();\n        return fr.getDeclaringType().isEquivalentTo(td);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/SyncOnUpdatedField.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"Multithreading\", name=\"SynchronizationOnUpdatedField\", maxScore=65)\npublic class SyncOnUpdatedField {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc) {\n        if(expr.getCode() == AstCode.PutField || expr.getCode() == AstCode.PutStatic) {\n            FieldReference fr = (FieldReference) expr.getOperand();\n            while(nc != null) {\n                Expression syncObject = nc.getSyncObject();\n                if(syncObject != null) {\n                    if(syncObject.getCode() == AstCode.GetField && expr.getCode() == AstCode.PutField) {\n                        FieldReference sfr = (FieldReference) syncObject.getOperand();\n                        if(sfr.isEquivalentTo(fr) && Nodes.isEquivalent(Nodes.getChild(expr, 0), Nodes.getChild(syncObject, 0))) {\n                            mc.report(\"SynchronizationOnUpdatedField\", 0, expr);\n                        }\n                    } else if(syncObject.getCode() == AstCode.GetStatic && expr.getCode() == AstCode.PutStatic) {\n                        FieldReference sfr = (FieldReference) syncObject.getOperand();\n                        if(sfr.isEquivalentTo(fr)) {\n                            mc.report(\"SynchronizationOnUpdatedField\", 0, expr);\n                        }\n                    }\n                }\n                nc = nc.getParent();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/ToArrayDowncast.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Role.TypeRole;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"ImpossibleToArrayDowncast\", maxScore = 65)\npublic class ToArrayDowncast {\n    private static final TypeRole TARGET_ELEMENT_TYPE = TypeRole.forName(\"TARGET_ELEMENT_TYPE\");\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if (expr.getCode() != AstCode.CheckCast)\n            return;\n        TypeReference targetType = (TypeReference) expr.getOperand();\n        if (!targetType.isArray() || Types.isObject(targetType.getElementType()))\n            return;\n        Expression arg = Exprs.getChild(expr, 0);\n        if (arg.getCode() != AstCode.InvokeVirtual && arg.getCode() != AstCode.InvokeInterface)\n            return;\n        MethodReference mr = (MethodReference) arg.getOperand();\n        if (!mr.getName().equals(\"toArray\") || !mr.getSignature().equals(\"()[Ljava/lang/Object;\"))\n            return;\n        Expression target = Exprs.getChild(arg, 0);\n        if (!Types.isInstance(target.getInferredType(), \"java/util/Collection\"))\n            return;\n        mc.report(\"ImpossibleToArrayDowncast\", 0, target, Roles.TARGET_TYPE.create(targetType),\n            TARGET_ELEMENT_TYPE.create(targetType.getElementType()));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/UncalledPrivateMethod.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport com.strobel.assembler.ir.Instruction;\nimport com.strobel.assembler.metadata.DynamicCallSite;\nimport com.strobel.assembler.metadata.MethodBody;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodHandle;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport one.util.huntbugs.registry.AbstractTypeDatabase;\nimport one.util.huntbugs.registry.ClassContext;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.TypeDatabase;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Annotations;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"RedundantCode\", name=\"UncalledPrivateMethod\", maxScore=45)\n@WarningDefinition(category=\"RedundantCode\", name=\"UncalledMethodOfAnonymousClass\", maxScore=45)\n@WarningDefinition(category=\"RedundantCode\", name=\"UncalledPrivateMethodChain\", maxScore=50)\npublic class UncalledPrivateMethod {\n    \n    @TypeDatabase\n    public static class NestedAnonymousCalls extends AbstractTypeDatabase<Void> {\n        Set<MemberInfo> mis = new HashSet<>();\n        \n        public NestedAnonymousCalls() {\n            super(tr -> null);\n        }\n\n        @Override\n        protected void visitType(TypeDefinition td) {\n            TypeReference tr = td.getDeclaringType();\n            if(tr == null) return;\n            TypeDefinition outer = tr.resolve();\n            if(outer == null || !outer.isAnonymous()) return;\n            for(MethodDefinition md : td.getDeclaredMethods()) {\n                extractCalls(md, mr -> {\n                    mis.add(new MemberInfo(mr));\n                    return true;\n                });\n            }\n        }\n        \n        public boolean isCalled(MemberInfo mi) {\n            return mis.contains(mi);\n        }\n    }\n    \n    private final Map<MemberInfo, Set<MemberInfo>> candidates = new LinkedHashMap<>();\n    \n    @ClassVisitor\n    public void visitType(TypeDefinition td, ClassContext cc, NestedAnonymousCalls nac) {\n        if(Types.isInstance(td, \"com/sun/jna/Callback\"))\n            return;\n        for(MethodDefinition md : td.getDeclaredMethods()) {\n            if(md.isPrivate() && !md.isSpecialName() \n                    && !Methods.isSerializationMethod(md)\n                    && !md.getName().toLowerCase(Locale.ENGLISH).contains(\"debug\")\n                    && !md.getName().toLowerCase(Locale.ENGLISH).contains(\"trace\")\n                    && !Annotations.hasAnnotation(md, true)) {\n                candidates.put(new MemberInfo(md), new HashSet<>());\n            }\n        }\n        if (td.isAnonymous() && !td.isSynthetic() && !td.getSimpleName().contains(\"$_invokeMethod_\") && Types\n                .hasCompleteHierarchy(td)) {\n            for(MethodDefinition md : td.getDeclaredMethods()) {\n                if (!md.isSpecialName() && !md.isPrivate() && !md.isSynthetic() && Methods.findSuperMethod(\n                    md) == null) {\n                    MemberInfo mi = new MemberInfo(md);\n                    if(!nac.isCalled(mi)) {\n                        candidates.put(mi, new HashSet<>());\n                    }\n                }\n            }\n        }\n        for(MethodDefinition md : td.getDeclaredMethods()) {\n            if(candidates.isEmpty())\n                return;\n            extractCalls(md, mr -> {\n                link(md, mr);\n                return !candidates.isEmpty();\n            });\n        }\n        while(!candidates.isEmpty()) {\n            MemberInfo mi = candidates.keySet().iterator().next();\n            Set<MemberInfo> called = new HashSet<>(candidates.remove(mi));\n            boolean changed = true;\n            while(changed) {\n                changed = false;\n                for(MemberInfo callee : called) {\n                    Set<MemberInfo> called2 = candidates.remove(callee);\n                    if(called2 != null && called.addAll(called2)) {\n                        changed = true;\n                        break;\n                    }\n                }\n            }\n            if(td.isAnonymous()) {\n                cc.report(\"UncalledMethodOfAnonymousClass\", 0, Roles.METHOD.create(mi));\n            } else if(called.isEmpty()) {\n                cc.report(\"UncalledPrivateMethod\", 0, Roles.METHOD.create(mi));\n            } else {\n                cc.report(\"UncalledPrivateMethodChain\", 0, Stream.concat(Stream.of(Roles.METHOD.create(mi)),\n                    called.stream()\n                        .filter(c -> !c.equals(mi))\n                        .map(Roles.CALLED_METHOD::create))\n                        .toArray(WarningAnnotation[]::new));\n            }\n        }\n    }\n    \n    static void extractCalls(MethodDefinition md, Predicate<MethodReference> action) {\n        MethodBody body = md.getBody();\n        if(body == null)\n            return;\n        for(Instruction inst : body.getInstructions()) {\n            for(int i=0; i<inst.getOperandCount(); i++) {\n                Object operand = inst.getOperand(i);\n                if(operand instanceof MethodReference) {\n                    if(!action.test((MethodReference)operand))\n                        return;\n                }\n                if(operand instanceof DynamicCallSite) {\n                    MethodHandle mh = Nodes.getMethodHandle((DynamicCallSite) operand);\n                    if(mh != null) {\n                        if(!action.test(mh.getMethod()))\n                            return;\n                    }\n                }\n            }\n        }\n    }\n    \n    private void link(MethodReference from, MethodReference to) {\n        MemberInfo miTo = new MemberInfo(to);\n        if(!candidates.containsKey(miTo))\n            return;\n        MemberInfo miFrom = new MemberInfo(from);\n        Set<MemberInfo> curCandidate = candidates.get(miFrom);\n        if(curCandidate == null) {\n            remove(miTo);\n        } else {\n            curCandidate.add(miTo);\n        }\n    }\n\n    private void remove(MemberInfo mi) {\n        Set<MemberInfo> called = candidates.remove(mi);\n        candidates.values().forEach(set -> set.remove(mi));\n        if(called != null) {\n            called.forEach(this::remove);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/UnnecessaryBoxing.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.Role.LocationRole;\nimport one.util.huntbugs.warning.Role.TypeRole;\nimport one.util.huntbugs.warning.WarningAnnotation.Location;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Performance\", name = \"BoxedForToString\", maxScore = 30)\n@WarningDefinition(category = \"Performance\", name = \"BoxedForUnboxing\", maxScore = 30)\n@WarningDefinition(category = \"Performance\", name = \"UnboxedForBoxing\", maxScore = 45)\npublic class UnnecessaryBoxing {\n    private static final LocationRole BOXED_AT = LocationRole.forName(\"BOXED_AT\");\n    private static final TypeRole BOXED_TYPE = TypeRole.forName(\"BOXED_TYPE\");\n    \n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc) {\n        if (Nodes.isUnboxing(expr)) {\n            Expression arg = expr.getArguments().get(0);\n            List<Expression> usages = Inf.BACKLINK.findTransitiveUsages(expr, false).collect(Collectors.toList());\n            if (!usages.isEmpty()) {\n                TypeReference type = ((MethodReference) expr.getOperand()).getDeclaringType();\n                List<WarningAnnotation<?>> annotations = usages.stream().filter(\n                    e -> isBoxing(e) && e.getInferredType().isEquivalentTo(arg.getInferredType())).map(\n                    e -> BOXED_AT.create(mc, e)).collect(Collectors.toList());\n                if (annotations.size() == usages.size()) {\n                    annotations.add(BOXED_TYPE.create(type));\n                    JvmType simpleType = expr.getInferredType().getSimpleType();\n                    mc.report(\"UnboxedForBoxing\", simpleType == JvmType.Boolean || simpleType == JvmType.Byte ? 15 : 0,\n                        arg, annotations.toArray(new WarningAnnotation<?>[0]));\n                }\n            }\n        } else if (isBoxing(expr)) {\n            Expression arg = expr.getArguments().get(0);\n            List<Expression> usages = Inf.BACKLINK.findTransitiveUsages(expr, false).collect(Collectors.toList());\n            if (!usages.isEmpty()) {\n                if (usages.stream().allMatch(ex -> Nodes.isUnboxing(ex) || isBoxedToString(ex))) {\n                    TypeReference type = ((MethodReference) expr.getOperand()).getDeclaringType();\n                    Map<Boolean, List<Expression>> map = usages.stream().collect(\n                        Collectors.partitioningBy(Nodes::isUnboxing));\n                    if (!map.get(true).isEmpty()) {\n                        List<WarningAnnotation<?>> annotations = getUsedLocations(arg, mc, map.get(true));\n                        annotations.add(BOXED_TYPE.create(type));\n                        mc.report(\"BoxedForUnboxing\", 0, arg, annotations.toArray(new WarningAnnotation<?>[0]));\n                    }\n                    if (!map.get(false).isEmpty()) {\n                        List<WarningAnnotation<?>> annotations = getUsedLocations(arg, mc, map.get(false));\n                        annotations.add(BOXED_TYPE.create(type));\n                        annotations.add(Roles.REPLACEMENT_METHOD.create(\"java/lang/String\", \"valueOf\", \"(\" + arg\n                                .getInferredType().getInternalName() + \")Ljava/lang/String;\"));\n                        mc.report(\"BoxedForToString\", 0, arg, annotations.toArray(new WarningAnnotation<?>[0]));\n                    }\n                }\n            }\n        }\n    }\n\n    private List<WarningAnnotation<?>> getUsedLocations(Expression arg, MethodContext mc, List<Expression> list) {\n        Location curLoc = mc.getLocation(arg);\n        List<WarningAnnotation<?>> annotations = list.stream().map(mc::getLocation).filter(\n            loc -> loc.getSourceLine() != curLoc.getSourceLine() && loc.getSourceLine() != -1).map(\n            loc -> Roles.USED_AT.create(loc)).collect(Collectors.toList());\n        return annotations;\n    }\n\n    private boolean isBoxedToString(Expression expr) {\n        if (expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if (mr.getName().equals(\"toString\") && mr.getSignature().equals(\"()Ljava/lang/String;\")) {\n                TypeReference type = mr.getDeclaringType();\n                if (Types.isBoxed(type)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    private boolean isBoxing(Expression expr) {\n        if (Nodes.isBoxing(expr))\n            return true;\n        if (expr.getCode() == AstCode.InitObject) {\n            MethodReference ctor = (MethodReference) expr.getOperand();\n            // like \"(I)V\"\n            return Types.isBoxed(ctor.getDeclaringType()) && ctor.getSignature().length() == 4;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/UnnecessaryInstanceOf.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.MetadataHelper;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.flow.CFG.EdgeType;\nimport one.util.huntbugs.flow.CodeBlock;\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.flow.etype.EType;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.YesNoMaybe;\nimport one.util.huntbugs.warning.Role.StringRole;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"RedundantCode\", name = \"UnnecessaryInstanceOf\", maxScore = 50)\n@WarningDefinition(category = \"Correctness\", name = \"ImpossibleInstanceOf\", maxScore = 70)\n@WarningDefinition(category = \"Correctness\", name = \"ImpossibleCast\", maxScore = 70)\n@WarningDefinition(category = \"Correctness\", name = \"ClassComparisonFalse\", maxScore = 70)\npublic class UnnecessaryInstanceOf {\n    private static final StringRole ETYPE = StringRole.forName(\"ETYPE\");\n\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression node, MethodContext mc, TypeDefinition td) {\n        if (node.getCode() == AstCode.InstanceOf) {\n            TypeReference typeRef = (TypeReference) node.getOperand();\n            Expression expr = node.getArguments().get(0);\n            EType eType = Inf.ETYPE.resolve(expr);\n            YesNoMaybe ynm = eType.is(typeRef, false);\n            if (ynm.yes()) {\n                report(mc, typeRef, expr, null, null, eType.shrinkConstraint(typeRef, false), \"UnnecessaryInstanceOf\");\n            } else if (ynm.no()) {\n                // Autogenerated by javacc\n                if (expr.getOperand() instanceof Variable && ((Variable) expr.getOperand()).getName().startsWith(\"jjte\")\n                    && td.getName().endsWith(\"Parser\"))\n                    return;\n                report(mc, typeRef, expr, node, EdgeType.TRUE, eType.shrinkConstraint(typeRef, false),\n                    \"ImpossibleInstanceOf\");\n            }\n        } else if (node.getCode() == AstCode.CheckCast) {\n            TypeReference typeRef = MetadataHelper.erase((TypeReference) node.getOperand());\n            Expression expr = node.getArguments().get(0);\n            EType eType = Inf.ETYPE.resolve(expr);\n            YesNoMaybe ynm = eType.is(typeRef, false);\n            if (ynm.no()) {\n                report(mc, typeRef, expr, null, null, eType.shrinkConstraint(typeRef, false), \"ImpossibleCast\");\n            }\n        } else if (node.getCode() == AstCode.CmpEq || node.getCode() == AstCode.InvokeVirtual && Methods.isEqualsMethod(\n            (MethodReference) node.getOperand())) {\n            Nodes.ifBinaryWithConst(node, (arg, cst) -> {\n                if (cst instanceof TypeReference) {\n                    arg = ValuesFlow.getSource(arg);\n                    if (arg.getCode() == AstCode.InvokeVirtual && Methods.isGetClass((MethodReference) arg\n                            .getOperand())) {\n                        Expression obj = arg.getArguments().get(0);\n                        EType eType = Inf.ETYPE.resolve(obj);\n                        TypeReference typeRef = (TypeReference) cst;\n                        YesNoMaybe ynm = eType.is(typeRef, true);\n                        if (ynm.no()) {\n                            report(mc, typeRef, obj, node, EdgeType.TRUE, eType.shrinkConstraint(typeRef, true),\n                                \"ClassComparisonFalse\");\n                        }\n                    }\n                }\n            });\n        }\n    }\n\n    private void report(MethodContext mc, TypeReference typeRef, Expression expr, Expression cond, EdgeType deadEdge,\n            EType eType, String wType) {\n        int priority = 0;\n        List<WarningAnnotation<?>> anno = new ArrayList<>(Arrays.asList(Roles.TARGET_TYPE.create(typeRef), ETYPE.create(\n            eType.toString()), Roles.EXPRESSION.create(expr)));\n        if (deadEdge != null) {\n            CodeBlock deadCode = mc.findDeadCode(cond, deadEdge);\n            if (deadCode != null) {\n                priority = deadCode.isExceptional ? 40 : 0;\n                anno.add(Roles.DEAD_CODE_LOCATION.create(mc, deadCode.startExpr));\n            } else {\n                priority = 20;\n            }\n        }\n        mc.report(wType, priority, expr, anno);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/UnreachableCatch.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.decompiler.ast.CatchBlock;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"RedundantCode\", name=\"UnreachableCatch\", maxScore=50)\npublic class UnreachableCatch {\n    @AstVisitor\n    public void visit(Node node, MethodContext mc) {\n        if(node instanceof CatchBlock) {\n            Expression firstExpr = (Expression) Nodes.find(node, Expression.class::isInstance);\n            if(firstExpr != null && !mc.isReachable(firstExpr)) {\n                mc.report(\"UnreachableCatch\", 0, firstExpr);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/UnsafeGetResource.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.db.Hierarchy.TypeHierarchy;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category=\"BadPractice\", name=\"UnsafeGetResource\", maxScore=60)\npublic class UnsafeGetResource {\n    @MethodVisitor\n    public boolean checkMethod(MethodDefinition md, TypeDefinition td) {\n        return td.isPublic() && !td.isFinal() && !md.isStatic();\n    }\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, MethodContext mc, TypeHierarchy th) {\n        if(expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference getResourceCall = (MethodReference) expr.getOperand();\n            if((getResourceCall.getName().equals(\"getResource\") || getResourceCall.getName().equals(\"getResourceAsStream\")) &&\n                    getResourceCall.getDeclaringType().getInternalName().equals(\"java/lang/Class\")) {\n                Expression classObj = Exprs.getChild(expr, 0);\n                if(classObj.getCode() == AstCode.InvokeVirtual) {\n                    MethodReference getClassCall = (MethodReference) classObj.getOperand();\n                    if(Methods.isGetClass(getClassCall)) {\n                        if(Exprs.isThis(Exprs.getChild(classObj, 0))) {\n                            Object resource = Nodes.getConstant(expr.getArguments().get(1));\n                            int priority = 0;\n                            if (resource instanceof String && ((String)resource).startsWith(\"/\"))\n                                priority += 30;\n                            else {\n                                if (th != null) {\n                                    if (!th.hasSubClasses())\n                                        priority += 20;\n                                    else if (!th.hasSubClassesOutOfPackage())\n                                        priority += 10;\n                                }\n                            }\n                            mc.report(\"UnsafeGetResource\", priority, expr, Roles.ARGUMENT.create(expr.getArguments().get(1)));\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/UnsupportedCall.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.Locale;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.db.MethodStats;\nimport one.util.huntbugs.db.MethodStats.MethodData;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.AccessLevel;\nimport one.util.huntbugs.util.NodeChain;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category = \"CodeStyle\", name = \"UnsupportedCall\", maxScore = 50)\npublic class UnsupportedCall {\n    @MethodVisitor\n    public boolean check(MethodDefinition md) {\n        return !md.isSynthetic();\n    }\n    \n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc, MethodStats ms) {\n        if(expr.getOperand() instanceof MethodReference) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            MethodData stats = ms.getStats(mr);\n            boolean exact = expr.getCode() == AstCode.InitObject || expr.getCode() == AstCode.InvokeSpecial;\n            if (stats != null\n                && stats.testAny(MethodStats.METHOD_HAS_BODY, exact)\n                && !stats.testAny(MethodStats.METHOD_SUPPORTED, exact)) {\n                if(mr.getDeclaringType().getPackageName().equals(\"java.lang\")) {\n                    return;\n                }\n                String lcName = mr.getName().toLowerCase(Locale.ENGLISH);\n                if(lcName.contains(\"unsupported\") || lcName.contains(\"throw\") || lcName.contains(\"exception\") || lcName.contains(\"error\"))\n                    return;\n                // Subclassed in SPI which might be out of the analysis scope\n                if(mr.getDeclaringType().getInternalName().equals(\"java/nio/charset/CharsetDecoder\"))\n                    return;\n                if(nc.isInTry(\"java/lang/UnsupportedOperationException\")) {\n                    return;\n                }\n                int priority = 0;\n                if(expr.getCode() == AstCode.InvokeInterface) {\n                    priority = 20;\n                } else if(expr.getCode() == AstCode.InvokeVirtual) {\n                    MethodDefinition md = mr.resolve();\n                    if(md != null && !md.isFinal() && !md.getDeclaringType().isFinal()) {\n                        priority = AccessLevel.of(md).select(25, 15, 0, 0);\n                    }\n                }\n                mc.report(\"UnsupportedCall\", priority, expr);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/UnusedParameter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.db.Hierarchy;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.AccessLevel;\nimport one.util.huntbugs.util.Annotations;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author shustkost\n *\n */\n@WarningDefinition(category = \"Correctness\", name = \"ConstructorParameterIsNotPassed\", maxScore = 65)\n@WarningDefinition(category = \"Correctness\", name = \"MethodParameterIsNotPassed\", maxScore = 65)\n@WarningDefinition(category = \"Correctness\", name = \"ParameterOverwritten\", maxScore = 60)\n@WarningDefinition(category = \"RedundantCode\", name = \"MethodParameterIsNotUsed\", maxScore = 35)\npublic class UnusedParameter {\n    @ClassVisitor\n    public boolean checkClass(TypeDefinition td) {\n        // Exclude incomplete classes as we cannot reliably check whether the method is overridden\n        return Types.hasCompleteHierarchy(td);\n    }\n    \n    @AstVisitor(nodes = AstNodes.ROOT)\n    public void visitBody(Block block, MethodContext mc, MethodDefinition md, TypeDefinition td, Hierarchy h) {\n        if (md.isSynthetic() || !mc.isAnnotated() || Methods.isSerializationMethod(md) || Methods.isMain(md)\n                || isEmpty(block.getBody()) || Nodes.isThrow(block))\n            return;\n        for (ParameterDefinition pd : md.getParameters()) {\n            if (!pd.hasName())\n                continue;\n            Set<Expression> usages = mc.getParameterUsages(pd);\n            if (usages != null && usages.isEmpty()) {\n                // looks like autogenerated by jay\n                if (pd.getName().equals(\"yyVal\") && Types.isObject(pd.getParameterType()))\n                    continue;\n                // looks like autogenerated by protobuf\n                if (Types.isInstance(td, \"com/google/protobuf/GeneratedMessage\"))\n                    return;\n                // JNA callback\n                if (Types.isInstance(td, \"com/sun/jna/Callback\"))\n                    return;\n                if (pd.getName().equals(\"it\")) {\n                    String sourceFile = Types.getSourceFile(td);\n                    if (sourceFile != null && sourceFile.endsWith(\".groovy\"))\n                        return;\n                }\n                Expression overloadCall = findOverloadCall(md, block);\n                if (overloadCall != null) {\n                    MethodDefinition overloadMethod = ((MethodReference) overloadCall.getOperand()).resolve();\n                    if (overloadMethod != null) {\n                        ParameterDefinition anotherParam = overloadMethod.getParameters().stream().filter(apd -> apd\n                                .getName().equals(pd.getName()) && apd.getParameterType().isEquivalentTo(pd\n                                        .getParameterType())).findFirst().orElse(null);\n                        if (anotherParam != null) {\n                            int priority = 0;\n                            Expression arg = overloadCall.getArguments().get(overloadMethod.getParameters().indexOf(\n                                anotherParam) + (md.isStatic() ? 0 : 1));\n                            if (Nodes.getConstant(arg) == null && arg.getCode() != AstCode.AConstNull) {\n                                priority += 10;\n                            }\n                            mc.report(md.isConstructor() ? \"ConstructorParameterIsNotPassed\" : \"MethodParameterIsNotPassed\",\n                                priority, overloadCall, Roles.VARIABLE.create(pd.getName()));\n                            continue;\n                        }\n                    }\n                }\n                Node overwrite = Nodes.find(block, n -> {\n                    if (!(n instanceof Expression))\n                        return false;\n                    Expression expr = (Expression) n;\n                    return expr.getCode() == AstCode.Store && ((Variable) expr.getOperand())\n                            .getOriginalParameter() == pd;\n                });\n                if (overwrite != null) {\n                    mc.report(\"ParameterOverwritten\", Methods.findSuperMethod(md) == null ? 0 : 20, overwrite);\n                    continue;\n                }\n                if (Annotations.hasAnnotation(pd, false) || Methods.findSuperMethod(md) != null || h.isOverridden(md)) {\n                    continue;\n                }\n                int priority = md.isConstructor() || md.isStatic() ? AccessLevel.of(md).select(10, 7, 5, 0)\n                        : AccessLevel.of(md).select(20, 15, 5, 0);\n                if(md.isDeprecated())\n                    priority += 10;\n                if(block.getBody().size() == 1) {\n                    Node stmt = block.getBody().get(0);\n                    if(Nodes.isOp(stmt, AstCode.Return)) {\n                        List<Expression> args = ((Expression)stmt).getArguments();\n                        if(args.size() == 1 && Nodes.getConstant(args.get(0)) != null) {\n                            priority += 10;\n                        }\n                    }\n                }\n                mc.report(\"MethodParameterIsNotUsed\", priority, Roles.VARIABLE.create(pd.getName()));\n            }\n        }\n    }\n\n    private boolean isEmpty(List<Node> body) {\n        if(body.isEmpty())\n            return true;\n        if(body.size() == 1) {\n            Node n = body.get(0);\n            if(Nodes.isOp(n, AstCode.Return)) {\n                Expression e = (Expression) n;\n                if(e.getArguments().size() == 1) {\n                    Expression arg = e.getArguments().get(0);\n                    if(arg.getCode() == AstCode.AConstNull)\n                        return true;\n                    if(arg.getCode() == AstCode.LdC) {\n                        Object value = arg.getOperand();\n                        if(value != null && (value.equals(Boolean.FALSE) || value instanceof Number && ((Number)value).doubleValue() == 0.0))\n                            return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    private Expression findOverloadCall(MethodDefinition md, Block block) {\n        return (Expression)Nodes.find(block, node -> {\n            if (Nodes.isOp(node, md.isConstructor() ? AstCode.InvokeSpecial\n                    : md.isStatic() ? AstCode.InvokeStatic : AstCode.InvokeVirtual)) {\n                Expression expr = (Expression) node;\n                MethodReference mr = (MethodReference) expr.getOperand();\n                if (mr.getDeclaringType().isEquivalentTo(md.getDeclaringType()) && mr.getName().equals(md.getName())\n                    && !mr.getSignature().equals(md.getSignature())) {\n                    return true;\n                }\n            }\n            return false;\n        });\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/UselessVoidMethod.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.db.MethodStats;\nimport one.util.huntbugs.db.MethodStats.MethodData;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"RedundantCode\", name=\"UselessVoidMethod\", maxScore=50)\npublic class UselessVoidMethod {\n    @AstVisitor(nodes=AstNodes.ROOT)\n    public void checkMethod(Block root, MethodDefinition md, MethodStats ms, MethodContext mc) {\n        if(md.getReturnType().isVoid()) {\n            MethodData stats = ms.getStats(md);\n            if (stats != null && !stats.mayHaveSideEffect(true) && !stats.testAny(MethodStats.METHOD_MAY_THROW, true)) {\n                List<Node> body = root.getBody();\n                if(body.isEmpty())\n                    return;\n                if(md.isConstructor() && body.size() == 1 && Nodes.isOp(body.get(0), AstCode.InvokeSpecial))\n                    return;\n                int codeSize = Nodes.estimateCodeSize(root);\n                int priority = codeSize < 10 ? 20 : codeSize < 20 ? 10 : codeSize < 30 ? 5 : 0;\n                mc.report(\"UselessVoidMethod\", priority);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/VolatileArray.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.JvmType;\nimport one.util.huntbugs.db.FieldStats;\nimport one.util.huntbugs.registry.FieldContext;\nimport one.util.huntbugs.registry.anno.FieldVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author lan\n *\n */\n@WarningDefinition(category=\"Multithreading\", name=\"VolatileArray\", maxScore=50)\npublic class VolatileArray {\n    @FieldVisitor\n    public void checkField(FieldDefinition fd, FieldStats fs, FieldContext fc) {\n        if(fd.getFieldType().isArray() && Flags.testAny(fd.getFlags(), Flags.VOLATILE)) {\n            int priority = 0;\n            int flags = fs.getFlags(fd);\n            if(!Flags.testAny(flags, FieldStats.UNRESOLVED)) {\n                if(!Flags.testAny(flags, FieldStats.WRITE_NONNULL))\n                    return; // will be reported as unwritten\n                if(Flags.testAny(flags, FieldStats.WRITE_CLASS | FieldStats.WRITE_PACKAGE | FieldStats.WRITE_OUTSIDE)) {\n                    priority += 20; \n                }\n            }\n            fc.report(\"VolatileArray\", priority, Roles.REPLACEMENT_CLASS.create(getAtomicArrayReplacement(fd\n                    .getFieldType().getElementType().getSimpleType())));\n        }\n    }\n\n    private String getAtomicArrayReplacement(JvmType simpleType) {\n        switch(simpleType) {\n        case Boolean:\n        case Byte:\n        case Character:\n        case Integer:\n            return \"java/util/concurrent/atomic/AtomicIntegerArray\";\n        case Long:\n            return \"java/util/concurrent/atomic/AtomicLongArray\";\n        default:\n            return \"java/util/concurrent/atomic/AtomicReferenceArray\";\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/VolatileIncrement.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Multithreading\", name = \"VolatileIncrement\", maxScore = 85)\n@WarningDefinition(category = \"Multithreading\", name = \"VolatileMath\", maxScore = 85)\npublic class VolatileIncrement {\n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visitNode(Expression node, MethodContext ctx, NodeChain parents, MethodDefinition md) {\n        if (node.getCode() == AstCode.PreIncrement || node.getCode() == AstCode.PostIncrement) {\n            Expression arg = node.getArguments().get(0);\n            if (arg.getCode() == AstCode.GetField || arg.getCode() == AstCode.GetStatic) {\n                FieldDefinition field = ((FieldReference) arg.getOperand()).resolve();\n                if (field != null && Flags.testAny(field.getFlags(), Flags.VOLATILE))\n                    ctx.report(\"VolatileIncrement\", computePriority(field, md, parents), arg);\n            }\n        }\n        if (node.getCode() == AstCode.PutField || node.getCode() == AstCode.PutStatic) {\n            FieldDefinition field = ((FieldReference) node.getOperand()).resolve();\n            if (field != null && Flags.testAny(field.getFlags(), Flags.VOLATILE)) {\n                Expression self = Exprs.getThis(node);\n                Expression op = node.getArguments().get(node.getCode() == AstCode.PutStatic ? 0 : 1);\n                if (Nodes.isBinaryMath(op)) {\n                    List<Expression> opArgs = op.getArguments();\n                    Expression left = opArgs.get(0);\n                    Expression right = opArgs.get(1);\n                    if (left.getCode() == AstCode.GetField || left.getCode() == AstCode.GetStatic) {\n                        if (((FieldReference) left.getOperand()).resolve() == field\n                            && Nodes.isEquivalent(self, Exprs.getThis(left))) {\n                            ctx.report(op.getCode() == AstCode.Add ? \"VolatileIncrement\" : \"VolatileMath\",\n                                computePriority(field, md, parents), node);\n                        }\n                    }\n                    if (right.getCode() == AstCode.GetField || right.getCode() == AstCode.GetStatic) {\n                        if (((FieldReference) right.getOperand()).resolve() == field\n                            && Nodes.isEquivalent(self, Exprs.getThis(right))) {\n                            ctx.report(op.getCode() == AstCode.Add ? \"VolatileIncrement\" : \"VolatileMath\",\n                                computePriority(field, md, parents), node);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private int computePriority(FieldDefinition field, MethodDefinition md, NodeChain parents) {\n        int priority = 0;\n        JvmType type = field.getFieldType().getSimpleType();\n        if (type != JvmType.Long && type != JvmType.Double)\n            priority += 10;\n\n        if (Flags.testAny(md.getFlags(), Flags.SYNCHRONIZED) || parents.isSynchronized())\n            priority += 30;\n        return priority;\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/WaitContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.List;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Loop;\nimport com.strobel.decompiler.ast.LoopType;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Multithreading\", name = \"WaitUnconditional\", maxScore = 65)\n@WarningDefinition(category = \"Multithreading\", name = \"WaitNotInLoop\", maxScore = 65)\n@WarningDefinition(category = \"Multithreading\", name = \"NotifyNaked\", maxScore = 50)\npublic class WaitContract {\n    @MethodVisitor\n    public boolean checkMethod(MethodDefinition md) {\n        return !md.isSynthetic();\n    }\n    \n    @AstVisitor(nodes=AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain parents, MethodContext mc) {\n        if (expr.getCode() == AstCode.InvokeVirtual) {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if (mr.getName().equals(\"wait\") && Types.isObject(mr.getDeclaringType())) {\n                NodeChain cur = parents;\n                boolean sawCondition = false;\n                boolean sawLoop = false;\n                boolean sawSynchronizedBlock = false;\n                while (cur != null) {\n                    if (!sawSynchronizedBlock && cur.getNode() instanceof Condition) {\n                        sawCondition = true;\n                    }\n                    if (cur.getNode() instanceof Loop) {\n                        sawLoop = true;\n                        if (!sawSynchronizedBlock && ((Loop) cur.getNode()).getLoopType() == LoopType.PreCondition) {\n                            sawCondition = true;\n                        }\n                    }\n                    if (Nodes.isSynchorizedBlock(cur.getNode())) {\n                        sawSynchronizedBlock = true;\n                    }\n                    cur = cur.getParent();\n                }\n                if (!sawLoop || !sawCondition) {\n                    mc.report(sawCondition ? \"WaitNotInLoop\" : \"WaitUnconditional\", mr.getSignature().equals(\"()V\") ? 0\n                            : 15, expr.getArguments().get(0), Roles.CALLED_METHOD.create(mr));\n                }\n            }\n            if((mr.getName().equals(\"notify\") || mr.getName().equals(\"notifyAll\")) && mr.getSignature().equals(\"()V\")) {\n                if(parents.getNode() instanceof Block) {\n                    List<Node> body = ((Block) parents.getNode()).getBody();\n                    if (!body.isEmpty() && body.get(0) == expr\n                        && (body.size() == 1 || body.size() == 2 && Nodes.isOp(body.get(1), AstCode.MonitorExit))\n                        && parents.getParent() != null\n                        && Nodes.isSynchorizedBlock(parents.getParent().getNode())\n                        && !parents.getParent().getParent().isSynchronized()) {\n                        mc.report(\"NotifyNaked\", 0, expr.getArguments().get(0), Roles.CALLED_METHOD.create(mr));\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/detect/WrongMapIterator.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.detect;\n\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.stream.Collectors;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\n\n/**\n * @author Tagir Valeev\n *\n */\n@WarningDefinition(category = \"Performance\", name = \"WrongMapIterator\", maxScore = 48)\n@WarningDefinition(category = \"Performance\", name = \"WrongMapIteratorValues\", maxScore = 55)\npublic class WrongMapIterator {\n    @AstVisitor(nodes = AstNodes.EXPRESSIONS)\n    public void visit(Expression expr, NodeChain nc, MethodContext mc) {\n        MethodReference getMr = getCalledMethod(expr);\n        if (getMr == null || !getMr.getName().equals(\"get\"))\n            return;\n        Expression mapArg = Exprs.getChildNoSpecial(expr, 0);\n        TypeReference type = ValuesFlow.reduceType(Exprs.getChild(expr, 0));\n        if (!Types.isInstance(type, \"java/util/Map\") || Types.isInstance(type, \"java/util/EnumMap\"))\n            return;\n        Expression key = Exprs.getChild(expr, 1);\n        while (key.getCode() == AstCode.CheckCast || Nodes.isBoxing(key) || Nodes.isUnboxing(key))\n            key = Exprs.getChild(key, 0);\n        MethodReference nextMr = getCalledMethod(key);\n        if (nextMr == null || !nextMr.getName().equals(\"next\") || !Types.is(nextMr.getDeclaringType(), Iterator.class))\n            return;\n        Expression iter = Exprs.getChild(key, 0);\n        MethodReference iterMr = getCalledMethod(iter);\n        if (iterMr == null || !iterMr.getName().equals(\"iterator\"))\n            return;\n        Expression keySet = Exprs.getChild(iter, 0);\n        MethodReference keySetMr = getCalledMethod(keySet);\n        if (keySetMr == null || !keySetMr.getName().equals(\"keySet\"))\n            return;\n        if (!Nodes.isEquivalent(mapArg, Exprs.getChildNoSpecial(keySet, 0)))\n            return;\n        int priority = nc.isInLoop() ? 0 : 15;\n        if(mc.isAnnotated() && usedForGetOnly(key, mapArg)) {\n            mc.report(\"WrongMapIteratorValues\", priority, expr, Roles.REPLACEMENT_METHOD.create(\"java/util/Map\", \"values\", \"()Ljava/util/Collection;\"));\n        } else {\n            mc.report(\"WrongMapIterator\", priority, expr, Roles.REPLACEMENT_METHOD.create(\"java/util/Map\", \"entrySet\", \"()Ljava/util/Set;\"));\n        }\n    }\n    \n    private static boolean usedForGetOnly(Expression key, Expression mapArg) {\n        HashSet<Expression> usages = Inf.BACKLINK.findTransitiveUsages(key, true).collect(Collectors.toCollection(HashSet::new));\n        while(!usages.isEmpty()) {\n            Expression usage = usages.iterator().next();\n            usages.remove(usage);\n            if(usage.getCode() == AstCode.CheckCast || Nodes.isBoxing(usage) || Nodes.isUnboxing(usage)) {\n                Inf.BACKLINK.findTransitiveUsages(usage, true).forEach(usages::add);\n            } else {\n                MethodReference getMr = getCalledMethod(usage);\n                if (getMr == null || !getMr.getName().equals(\"get\"))\n                    return false;\n                if(!Nodes.isEquivalent(Exprs.getChildNoSpecial(usage, 0), mapArg))\n                    return false;\n            }\n        }\n        return true;\n    }\n\n    private static MethodReference getCalledMethod(Expression expr) {\n        return (expr.getCode() == AstCode.InvokeVirtual || expr.getCode() == AstCode.InvokeInterface)\n                ? (MethodReference) expr.getOperand() : null;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/filter/AnnotationFilters.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.filter;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.annotations.AnnotationElement;\nimport com.strobel.assembler.metadata.annotations.AnnotationParameter;\nimport com.strobel.assembler.metadata.annotations.ArrayAnnotationElement;\nimport com.strobel.assembler.metadata.annotations.ConstantAnnotationElement;\nimport com.strobel.assembler.metadata.annotations.CustomAnnotation;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class AnnotationFilters {\n    private static final Predicate<Warning> ALL_PASS = w -> true;\n    private static final Predicate<Warning> NO_PASS = w -> false;\n    \n    private static Predicate<Warning> and(Predicate<Warning> left, Predicate<Warning> right) {\n        if(left == ALL_PASS)\n            return right;\n        if(right == ALL_PASS)\n            return left;\n        if(left == NO_PASS || right == NO_PASS)\n            return NO_PASS;\n        return left.and(right);\n    }\n    \n    private static Set<String> getSuppressed(List<CustomAnnotation> annos) {\n        Set<String> filters = null;\n        for (CustomAnnotation ca : annos) {\n            String name = ca.getAnnotationType().getSimpleName();\n            if (!(name.startsWith(\"Suppress\") && name.endsWith(\"Warning\")))\n                continue;\n            for (AnnotationParameter par : ca.getParameters()) {\n                if (par.getMember().equals(\"value\")) {\n                    AnnotationElement ae = par.getValue();\n                    AnnotationElement[] elements = ae instanceof ArrayAnnotationElement ? ((ArrayAnnotationElement) ae)\n                            .getElements() : new AnnotationElement[] { ae };\n                    for (AnnotationElement child : elements) {\n                        if (child instanceof ConstantAnnotationElement) {\n                            Object value = ((ConstantAnnotationElement) child).getConstantValue();\n                            if (value instanceof String) {\n                                if (filters == null)\n                                    filters = new HashSet<>();\n                                filters.add((String) value);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return filters == null ? Collections.emptySet() : filters;\n    }\n    \n    private static Predicate<Warning> forSuppressed(String suppressed) {\n        if(suppressed.equals(\"all\") || suppressed.equals(\"*\"))\n            return NO_PASS;\n        if(suppressed.endsWith(\"*\")) {\n            String substr = suppressed.substring(0, suppressed.length()-1);\n            return w -> !w.getType().getName().startsWith(substr);\n        }\n        return w -> !w.getType().getName().equals(suppressed);\n    }\n    \n    private static Predicate<Warning> forSuppressed(Set<String> suppressed) {\n        return suppressed.stream().map(AnnotationFilters::forSuppressed).reduce(AnnotationFilters::and).orElse(ALL_PASS);\n    }\n\n    public static Predicate<Warning> forType(TypeDefinition td) {\n        Predicate<Warning> pred = forSuppressed(getSuppressed(td.getAnnotations()));\n        if(pred == NO_PASS)\n            return pred;\n        for(FieldDefinition fd : td.getDeclaredFields()) {\n            Set<String> suppressed = getSuppressed(fd.getAnnotations());\n            if(!suppressed.isEmpty()) {\n                Predicate<Warning> fieldPred = forSuppressed(suppressed);\n                MemberInfo mi = new MemberInfo(fd); \n                pred = and(pred, w -> !mi.equals(w.getAnnotation(Roles.FIELD)) || fieldPred.test(w));\n            }\n        }\n        for(MethodDefinition md : td.getDeclaredMethods()) {\n            Set<String> suppressed = getSuppressed(md.getAnnotations());\n            MemberInfo mi = new MemberInfo(md); \n            if(!suppressed.isEmpty()) {\n                Predicate<Warning> methodPred = forSuppressed(suppressed);\n                pred = and(pred, w -> !mi.equals(w.getAnnotation(Roles.METHOD)) || methodPred.test(w));\n            }\n            for(ParameterDefinition pd : md.getParameters()) {\n                suppressed = getSuppressed(pd.getAnnotations());\n                if(!suppressed.isEmpty()) {\n                    Predicate<Warning> paramPred = forSuppressed(suppressed);\n                    String name = pd.getName(); \n                    pred = and(pred, w -> !mi.equals(w.getAnnotation(Roles.METHOD))\n                        || !name.equals(w.getAnnotation(Roles.VARIABLE)) || paramPred.test(w));\n                }\n            }\n        }\n        return pred;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/Annotator.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.function.Consumer;\n\nimport one.util.huntbugs.util.Nodes;\n\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\n/**\n * @author shustkost\n *\n */\nabstract class Annotator<T> {\n    private final T defValue;\n    private final int idx;\n\n    protected Annotator(String name, T defValue) {\n        this.defValue = defValue;\n        this.idx = Annotators.register(name);\n    }\n    \n    protected static void forExpressions(Node node, Consumer<Expression> cons) {\n        for(Node child : Nodes.getChildren(node)) {\n            if(child instanceof Expression) {\n                cons.accept((Expression)child);\n            } else {\n                forExpressions(child, cons);\n            }\n        }\n    }\n\n    protected T get(Expression expr) {\n        @SuppressWarnings(\"unchecked\")\n        T data = (T) Annotators.get(expr, idx);\n        return data == null ? defValue : data;\n    }\n    \n    protected void putIfAbsent(Expression expr, T data) {\n        Annotators.replace(expr, idx, null, data);\n    }\n    \n    protected void put(Expression expr, T data) {\n        Annotators.put(expr, idx, data);\n    }\n    \n    protected void remove(Expression expr) {\n        Annotators.remove(expr, idx);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/Annotators.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.strobel.componentmodel.Key;\nimport com.strobel.decompiler.ast.Expression;\n\n/**\n * Annotators registry\n * \n * @author Tagir Valeev\n */\npublic class Annotators {\n    private Annotators() {\n    }\n    \n    private static final List<String> names = new ArrayList<>();\n    private static final Key<Object[]> hbData = Key.create(\"hb.data\");\n    \n    // Order of declaration might be important\n    \n    static int register(String name) {\n        if(names.contains(name))\n            throw new IllegalStateException(name);\n        names.add(name);\n        return names.size()-1;\n    }\n    \n    static Object get(Expression expr, int i) {\n        Object[] data = expr.getUserData(hbData);\n        return data == null ? null : data[i];\n    }\n    \n    static void put(Expression expr, int i, Object data) {\n        Object[] userData = expr.getUserData(hbData);\n        if(userData == null) {\n            userData = new Object[names.size()];\n            expr.putUserData(hbData, userData);\n        }\n        userData[i] = data;\n    }\n    \n    static void replace(Expression expr, int i, Object oldData, Object data) {\n        Object[] userData = expr.getUserData(hbData);\n        if(userData == null) {\n            userData = new Object[names.size()];\n            expr.putUserData(hbData, userData);\n        }\n        if(userData[i] == oldData)\n            userData[i] = data;\n    }\n    \n    static void remove(Expression expr, int i) {\n        Object[] userData = expr.getUserData(hbData);\n        if(userData != null) {\n            userData[i] = null;\n        }\n    }\n    \n    /**\n     * Debug output of all facts known for expression\n     * \n     * @param expr\n     * @return String describing the annotators\n     */\n    public static String facts(Expression expr) {\n        Object[] data = expr.getUserData(hbData);\n        if(data == null)\n            return \"{}\";\n        StringBuilder sb = new StringBuilder(\"{\\n\");\n        for(int i=0; i<names.size(); i++) {\n            if(data[i] == null)\n                continue;\n            sb.append(\"  \").append(i+1).append(\".\").append(names.get(i))\n                .append(\" = \").append(data[i]).append(\"\\n\");\n        }\n        sb.append(\"}\");\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/BackLinkAnnotator.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\n/**\n * @author shustkost\n *\n */\npublic class BackLinkAnnotator extends Annotator<Set<Expression>> {\n\n    public BackLinkAnnotator() {\n        super(\"backlink\", Collections.emptySet());\n    }\n    \n    void annotate(Node node) {\n        forExpressions(node, this::annotateBackLinks);\n        forExpressions(node, this::fixTernary);\n    }\n    \n    private void fixTernary(Expression expr) {\n        for(Expression child : expr.getArguments()) {\n            fixTernary(child);\n        }\n        if(expr.getCode() == AstCode.TernaryOp) {\n            Expression left = expr.getArguments().get(1);\n            Expression right = expr.getArguments().get(1);\n            Set<Expression> links = get(expr);\n            if(!(links instanceof HashSet))\n                links = new HashSet<>(links);\n            links.addAll(get(left));\n            links.addAll(get(right));\n            links.remove(expr);\n            put(expr, links);\n        }\n    }\n\n    private void annotateBackLinks(Expression expr) {\n        for(Expression child : expr.getArguments()) {\n            doLink(expr, child);\n            annotateBackLinks(child);\n        }\n        Expression source = Inf.SOURCE.get(expr);\n        if(source != null) {\n            link(expr, source);\n        }\n    }\n\n    private void link(Expression target, Expression source) {\n        if (source.getCode() == SourceAnnotator.PHI_TYPE || source.getCode() == SourceAnnotator.UPDATE_TYPE) {\n            source.getArguments().forEach(arg -> link(target, arg));\n            return;\n        }\n        doLink(target, source);\n    }\n\n    private void doLink(Expression target, Expression source) {\n        Set<Expression> set = get(source);\n        if (set.isEmpty()) {\n            put(source, Collections.singleton(target));\n        } else {\n            if (!(set instanceof HashSet)) {\n                set = new HashSet<>(set);\n                put(source, set);\n            }\n            set.add(target);\n        }\n    }\n\n    public Set<Expression> findUsages(Expression input) {\n        Set<Expression> set = get(input);\n        return set instanceof HashSet ? Collections.unmodifiableSet(set) : set;\n    }\n\n    public Stream<Expression> findTransitiveUsages(Expression expr, boolean includePhi) {\n        return findUsages(expr).stream().filter(includePhi ? x -> true : x -> !ValuesFlow.hasPhiSource(x))\n            .flatMap(x -> {\n                if(x.getCode() == AstCode.Store)\n                    return null;\n                if(x.getCode() == AstCode.Load)\n                    return findTransitiveUsages(x, includePhi);\n                if(x.getCode() == AstCode.TernaryOp && includePhi)\n                    return findTransitiveUsages(x, includePhi);\n                return Stream.of(x);\n            });\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/CFG.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.CaseBlock;\nimport com.strobel.decompiler.ast.CatchBlock;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Label;\nimport com.strobel.decompiler.ast.Lambda;\nimport com.strobel.decompiler.ast.Loop;\nimport com.strobel.decompiler.ast.LoopType;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Switch;\nimport com.strobel.decompiler.ast.TryCatchBlock;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class CFG {\n    private static final int BLOCKTYPE_UNKNOWN = -1;\n    private static final int BLOCKTYPE_EXIT = -2;\n    private static final int BLOCKTYPE_FAIL = -3;\n    private static final int BLOCKTYPE_IMPLICIT = -4;\n\n    static final TypeDefinition throwable = Types.lookupJdkType(\"java/lang/Throwable\");\n    static final TypeDefinition exception = Types.lookupJdkType(\"java/lang/Exception\");\n    static final TypeDefinition runtimeException = Types.lookupJdkType(\"java/lang/RuntimeException\");\n    static final TypeDefinition classCastException = Types.lookupJdkType(\"java/lang/ClassCastException\");\n    static final TypeDefinition nullPointerException = Types.lookupJdkType(\"java/lang/NullPointerException\");\n    static final TypeDefinition arrayIndexOutOfBoundsException = Types.lookupJdkType(\"java/lang/ArrayIndexOutOfBoundsException\");\n    static final TypeDefinition arrayStoreException = Types.lookupJdkType(\"java/lang/ArrayStoreException\");\n    static final TypeDefinition outOfMemoryError = Types.lookupJdkType(\"java/lang/OutOfMemoryError\");\n    static final TypeDefinition linkageError = Types.lookupJdkType(\"java/lang/LinkageError\");\n    static final TypeDefinition error = Types.lookupJdkType(\"java/lang/Error\");\n\n    final List<BasicBlock> blocks = new ArrayList<>();\n    final Map<Lambda, CFG> lambdas = new HashMap<>();\n    final BasicBlock closure;\n    final MethodDefinition md;\n    final Block body;\n    final BasicBlock entry, exit = new BasicBlock(BLOCKTYPE_EXIT), fail = new BasicBlock(BLOCKTYPE_FAIL), implicit = new BasicBlock(BLOCKTYPE_IMPLICIT);\n    final Map<Label, BasicBlock> labelTargets = new HashMap<>();\n    final List<List<BasicBlock>> dupExpr;\n    // Number of block till which CFG is forward-only\n    final int forwardTill;\n    final boolean hasUnreachable;\n\n    private CFG(MethodDefinition md, BasicBlock closure, Block methodBody) {\n        this.md = md;\n        this.body = methodBody;\n        this.closure = closure;\n        if (methodBody.getBody().isEmpty()) {\n            entry = exit;\n            hasUnreachable = false;\n            dupExpr = Collections.emptyList();\n        } else {\n            entry = new BasicBlock();\n            buildBlock(entry, exit, new OuterJumpContext(), methodBody);\n            verify();\n            inlineBooleans();\n            fixBlocks();\n            dupExpr = computeDupBlocks(findDuplicates(methodBody));\n            hasUnreachable = blocks.stream().anyMatch(bb -> !bb.reached);\n        }\n        this.forwardTill = computeForwardTill();\n    }\n    \n    private TrueFalse<List<BasicBlock>> getConditionalBranches(BasicBlock cond) {\n        int trueStart = cond.trueTarget.id;\n        int falseStart = cond.falseTarget.id;\n        if(trueStart == BLOCKTYPE_EXIT)\n            trueStart = blocks.size();\n        if(falseStart == BLOCKTYPE_EXIT)\n            falseStart = blocks.size();\n        if(trueStart < cond.id || falseStart < cond.id)\n            return null;\n        Integer START = 0;\n        Integer TRUE = 1;\n        Integer FALSE = 2;\n        Integer BOTH = 3;\n        graphSearch(new GraphSearch<Integer>() {\n            @Override\n            public Integer markStart(Expression expr, boolean isEntry) {\n                return expr == cond.expr ? START : null;\n            }\n\n            @Override\n            public Integer transfer(Integer orig, Expression from, EdgeType edge, Expression to) {\n                if(orig == START) {\n                    if(edge == EdgeType.TRUE)\n                        return TRUE;\n                    if(edge == EdgeType.FALSE)\n                        return FALSE;\n                    return BOTH;\n                }\n                return orig;\n            }\n\n            @Override\n            public Integer merge(Integer f1, Integer f2) {\n                if(f1 == null)\n                    return f2;\n                if(f2 == null)\n                    return f1;\n                return BOTH;\n            }\n        });\n        List<BasicBlock> trueBlocks = null;\n        List<BasicBlock> falseBlocks = null;\n        int trueEnd = -1, falseEnd = -1;\n        for(BasicBlock bb : blocks) {\n            Integer state = (Integer) bb.state;\n            if(state == null)\n                continue;\n            if(trueEnd != -1 && trueBlocks == null && state != TRUE) {\n                trueBlocks = blocks.subList(trueStart, trueEnd+1);\n            }\n            if(falseEnd != -1 && falseBlocks == null && state != FALSE) {\n                falseBlocks = blocks.subList(falseStart, falseEnd+1);\n            }\n            if(state == TRUE) {\n                if(bb.id < trueStart || trueBlocks != null)\n                    return null;\n                trueEnd = bb.id;\n            }\n            if(state == FALSE) {\n                if(bb.id < falseStart || falseBlocks != null)\n                    return null;\n                falseEnd = bb.id;\n            }\n        }\n        if(trueEnd != -1 && trueBlocks == null) {\n            trueBlocks = blocks.subList(trueStart, blocks.size());\n        }\n        if(falseEnd != -1 && falseBlocks == null) {\n            falseBlocks = blocks.subList(falseStart, blocks.size());\n        }\n        if(trueBlocks == null && falseBlocks == null) {\n            return null;\n        }\n        if(trueBlocks == null) {\n            trueBlocks = Collections.emptyList();\n        }\n        if(falseBlocks == null) {\n            falseBlocks = Collections.emptyList();\n        }\n        return new TrueFalse<>(trueBlocks, falseBlocks);\n    }\n\n    private void inlineBooleans() {\n        boolean changed = true;\n        while(changed) {\n            changed = false;\n            for (BasicBlock bb : blocks) {\n                if (bb.trueTarget == null)\n                    continue;\n                BasicBlock load = bb;\n                boolean invert = false;\n                while (load.expr.getCode() == AstCode.LogicalNot) {\n                    load = blocks.get(load.id - 1);\n                    invert = !invert;\n                }\n                if (load.expr.getCode() != AstCode.Load)\n                    continue;\n                TrueFalse<List<BasicBlock>> branches = getConditionalBranches(bb);\n                if (branches == null)\n                    continue;\n                Variable var = (Variable) load.expr.getOperand();\n                if(var.getOriginalParameter() != null)\n                    continue;\n                List<BasicBlock> stores = blocks.stream().filter(s -> s.expr.getCode() == AstCode.Store && var.equals(s.expr\n                        .getOperand())).collect(Collectors.toList());\n                if (stores.size() != 1)\n                    continue;\n                BasicBlock store = stores.get(0);\n                BasicBlock cond = blocks.get(store.id-1);\n                int copyStart = store.id + 1;\n                int copyEnd = load.id;\n                int domStart = -1;\n                if(!branches.trueState.isEmpty()) {\n                    domStart = branches.trueState.get(branches.trueState.size()-1).id+1;\n                }\n                if(!branches.falseState.isEmpty()) {\n                    domStart = Math.max(domStart, branches.falseState.get(branches.falseState.size()-1).id+1);\n                }\n                if(copyEnd < copyStart || domStart == -1)\n                    continue;\n                List<BasicBlock> falseCopy = copyBlocks(copyStart, copyEnd);\n                if(falseCopy == null)\n                    continue;\n                List<BasicBlock> trueCopy = blocks.subList(copyStart, copyEnd);\n                List<BasicBlock> newBlocks = new ArrayList<>();\n                newBlocks.addAll(blocks.subList(0, cond.id+1));\n                BasicBlock storeTrue = new BasicBlock(true, new Expression(AstCode.Store, var, -1, new Expression(AstCode.LdC, Boolean.TRUE, -1)));\n                BasicBlock storeFalse = new BasicBlock(true, new Expression(AstCode.Store, var, -1, new Expression(AstCode.LdC, Boolean.FALSE, -1)));\n                cond.passTarget = null;\n                cond.trueTarget = storeTrue;\n                cond.falseTarget = storeFalse;\n                List<BasicBlock> dom = blocks.subList(domStart, blocks.size());\n                BasicBlock domTarget = dom.isEmpty() ? exit : dom.get(0);\n                BasicBlock trueTarget = branches.trueState.isEmpty() ? domTarget : branches.trueState.get(0);\n                if(!trueCopy.isEmpty())\n                    trueTarget = trueCopy.get(0);\n                BasicBlock falseTarget = branches.falseState.isEmpty() ? domTarget : branches.falseState.get(0);\n                if(!falseCopy.isEmpty())\n                    falseTarget = falseCopy.get(0);\n                newBlocks.add(storeTrue);\n                if(invert) {\n                    storeTrue.addTarget(EdgeType.PASS, falseTarget);\n                    newBlocks.addAll(falseCopy);\n                    newBlocks.addAll(branches.falseState);\n                    newBlocks.add(storeFalse);\n                    storeFalse.addTarget(EdgeType.PASS, trueTarget);\n                    newBlocks.addAll(trueCopy);\n                    newBlocks.addAll(branches.trueState);\n                } else {\n                    storeTrue.addTarget(EdgeType.PASS, trueTarget);\n                    newBlocks.addAll(trueCopy);\n                    newBlocks.addAll(branches.trueState);\n                    newBlocks.add(storeFalse);\n                    storeFalse.addTarget(EdgeType.PASS, falseTarget);\n                    newBlocks.addAll(falseCopy);\n                    newBlocks.addAll(branches.falseState);\n                }\n                newBlocks.addAll(dom);\n                blocks.clear();\n                blocks.addAll(newBlocks);\n                renumBlocks();\n                verify();\n                changed = true;\n                break;\n            }\n        }\n    }\n    \n    private List<BasicBlock> copyBlocks(int copyStart, int copyEnd) {\n        if(copyStart==copyEnd)\n            return Collections.emptyList();\n        // TODO\n        return null;\n    }\n\n    private void renumBlocks() {\n        for(int i=0; i<blocks.size(); i++) {\n            blocks.get(i).id = i;\n        }\n    }\n\n    private List<List<BasicBlock>> computeDupBlocks(Collection<Set<Expression>> dupExpr) {\n        return dupExpr.stream().map(dupSet -> blocks.stream().filter(bb -> dupSet.contains(bb.expr)).collect(Collectors\n                .toList())).collect(Collectors.toList());\n    }\n\n    private Collection<Set<Expression>> findDuplicates(Block methodBody) {\n        Map<List<?>, Set<Expression>> result = new HashMap<>();\n        Annotator.forExpressions(methodBody, e -> Exprs.stream(e).filter(expr -> expr.getOffset() > 0).forEach(\n            expr -> result.computeIfAbsent(Arrays.asList(expr.getOffset(), expr.getCode(), expr.getArguments().size(), expr.getOperand()),\n                k -> new HashSet<>()).add(expr)));\n        result.values().removeIf(set -> set.size() < 2);\n        return result.values();\n    }\n\n    private void verify() {\n        for (BasicBlock bb : blocks) {\n            if (bb.trueTarget == null ^ bb.falseTarget == null) {\n                throw new IllegalStateException(\"Mismatch true/false targets CFG at [\" + bb.getId() + \"]: \" + this);\n            }\n            if (bb.targets().anyMatch(target -> target.id == -1)) {\n                throw new IllegalStateException(\"Not linked target block at [\" + bb.getId() + \"]: \" + this);\n            }\n            if (bb.targets().anyMatch(target -> target.id >= 0 && blocks.get(target.id) != target)) {\n                throw new IllegalStateException(\"Missing target block at [\" + bb.getId() + \"]: \" + this);\n            }\n            if (bb.reached && bb.targets().anyMatch(target -> !target.reached)) {\n                throw new IllegalStateException(\"Invalid flow control at [\" + bb.getId() + \"]: \" + this);\n            }\n        }\n    }\n\n    private void fixBlocks() {\n        entry.reached = true;\n        for (BasicBlock bb : blocks) {\n            while (bb.falseTarget != null && bb.falseTarget.expr != null && bb.falseTarget.expr\n                    .getCode() == AstCode.LogicalAnd)\n                bb.falseTarget = bb.falseTarget.falseOrPass();\n            while (bb.trueTarget != null && bb.trueTarget.expr != null && bb.trueTarget.expr\n                    .getCode() == AstCode.LogicalOr)\n                bb.trueTarget = bb.trueTarget.trueOrPass();\n            if(bb.synthetic) {\n                Expression expr = bb.expr;\n                if(expr.getCode() == AstCode.Store) {\n                    Variable var = (Variable) expr.getOperand();\n                    Expression arg = expr.getArguments().get(0);\n                    if(arg.getCode() == AstCode.LdC && arg.getOperand() instanceof Boolean) {\n                        boolean val = (boolean) arg.getOperand();\n                        while(true) {\n                            BasicBlock target = bb.passTarget;\n                            if(target == null)\n                                break;\n                            Expression targetExpr = target.expr;\n                            if(targetExpr == null)\n                                break;\n                            boolean wanted;\n                            if(targetExpr.getCode() == AstCode.LogicalAnd)\n                                wanted = false;\n                            else if(targetExpr.getCode() == AstCode.LogicalOr)\n                                wanted = true;\n                            else break;\n                            Expression targetArg = targetExpr.getArguments().get(0);\n                            while(targetArg.getCode() == AstCode.LogicalNot) {\n                                val = !val;\n                                targetArg = targetArg.getArguments().get(0);\n                            }\n                            if (wanted != val || targetArg.getCode() != AstCode.Load || !var.equals(targetArg.getOperand()))\n                                break;\n                            bb.passTarget = wanted ? target.trueOrPass() : target.falseOrPass();\n                        }\n                    }\n                }\n            }\n            if (bb.reached)\n                bb.targets().forEach(t -> t.reached = true);\n        }\n    }\n\n    private int computeForwardTill() {\n        int forwardTill = blocks.size();\n        for (BasicBlock bb : blocks) {\n            int min = bb.targets().mapToInt(t -> t.id).filter(id -> id >= 0 && id < bb.id).min().orElse(forwardTill);\n            if (min < forwardTill)\n                forwardTill = min;\n        }\n        return forwardTill;\n    }\n\n    private void buildBlock(BasicBlock entry, BasicBlock exit, JumpContext jc, Block block) {\n        BasicBlock curBlock = entry;\n        BasicBlock nextBlock = null;\n        Node prevNode = null;\n        List<Node> body = block.getBody();\n        if (body.isEmpty()) {\n            throw new IllegalStateException(\"Empty body is supplied!\");\n        }\n        if (body.size() == 1 && body.get(0) instanceof Label) {\n            buildSingleLabel(entry, exit, (Label) body.get(0));\n            return;\n        }\n        Set<Label> labels = new HashSet<>();\n        for (Node node : body) {\n            if (node instanceof Label) {\n                labels.add((Label) node);\n            }\n        }\n        if (!labels.isEmpty()) {\n            jc = new LabelJumpContext(jc, labels);\n        }\n        Node last = body.get(body.size() - 1);\n        if (last instanceof Label && labelTargets.put((Label) last, exit) != null) {\n            throw new IllegalStateException(\"Label \" + last + \" is already linked\");\n        }\n        for (Node node : body) {\n            if (node instanceof Label) {\n                Label label = (Label) node;\n                if (prevNode == null) {\n                    BasicBlock oldTarget = labelTargets.putIfAbsent(label, entry);\n                    if (oldTarget != null && oldTarget != entry) {\n                        oldTarget.setExpression(new Expression(AstCode.Goto, label, -1));\n                        oldTarget.addTarget(EdgeType.PASS, entry);\n                        register(oldTarget);\n                    }\n                } else {\n                    nextBlock = labelTargets.computeIfAbsent(label, k -> new BasicBlock());\n                }\n                continue;\n            }\n            if (nextBlock == null)\n                nextBlock = new BasicBlock();\n            if (prevNode != null) {\n                buildNode(curBlock, nextBlock, jc, prevNode);\n                curBlock = nextBlock;\n                nextBlock = null;\n            }\n            prevNode = node;\n        }\n        if (prevNode != null) {\n            buildNode(curBlock, exit, jc, prevNode);\n        }\n    }\n\n    private void buildSingleLabel(BasicBlock entry, BasicBlock exit, Label label) {\n        BasicBlock oldTarget = labelTargets.putIfAbsent(label, entry);\n        if (oldTarget != null && oldTarget != entry) {\n            oldTarget.setExpression(new Expression(AstCode.Goto, label, -1));\n            oldTarget.addTarget(EdgeType.PASS, entry);\n            register(oldTarget);\n        }\n        entry.setExpression(new Expression(AstCode.Goto, label, -1));\n        entry.addTarget(EdgeType.PASS, exit);\n        register(entry);\n        return;\n    }\n\n    private void buildNode(final BasicBlock curBlock, BasicBlock nextBlock, JumpContext jc, Node node) {\n        if (node instanceof Expression) {\n            BasicBlock lastBlock = buildExpr(curBlock, (Expression) node, jc);\n            if (lastBlock != null)\n                lastBlock.addTarget(EdgeType.PASS, nextBlock);\n        } else if (node instanceof Condition) {\n            buildCondition(curBlock, nextBlock, jc, (Condition) node);\n        } else if (node instanceof Loop) {\n            buildLoop(curBlock, nextBlock, jc, (Loop) node);\n        } else if (node instanceof Switch) {\n            buildSwitch(curBlock, nextBlock, jc, (Switch) node);\n        } else if (node instanceof TryCatchBlock) {\n            TryCatchBlock tryCatch = (TryCatchBlock) node;\n            if (tryCatch.getFinallyBlock() == null || tryCatch.getFinallyBlock().getBody().isEmpty()) {\n                buildTryCatch(curBlock, nextBlock, jc, tryCatch);\n            } else if (tryCatch.getTryBlock().getBody().isEmpty() && tryCatch.getCatchBlocks().isEmpty()) {\n                buildBlock(curBlock, nextBlock, jc, tryCatch.getFinallyBlock());\n            } else {\n                buildFinally(curBlock, nextBlock, jc, tryCatch);\n            }\n        }\n    }\n\n    private void buildFinally(final BasicBlock curBlock, BasicBlock nextBlock, JumpContext jc, TryCatchBlock tryCatch) {\n        BasicBlock finallyBlock = new BasicBlock();\n        FinallyJumpContext fjc = new FinallyJumpContext(jc);\n        buildTryCatch(curBlock, finallyBlock, fjc, tryCatch);\n        if (blocks.stream().flatMap(BasicBlock::targets).anyMatch(finallyBlock::equals)) {\n            buildBlock(finallyBlock, nextBlock, jc, tryCatch.getFinallyBlock());\n        }\n        fjc.targetEntries.forEach((target, entry) -> {\n            buildBlock(entry, target, jc, tryCatch.getFinallyBlock());\n        });\n    }\n\n    // Processes try-catch only, ignores finally, if any\n    private void buildTryCatch(final BasicBlock curBlock, BasicBlock nextBlock, JumpContext jc,\n            TryCatchBlock tryCatch) {\n        if (!tryCatch.getCatchBlocks().isEmpty()) {\n            CatchJumpContext cjc = new CatchJumpContext(jc, nextBlock, tryCatch.getCatchBlocks());\n            buildBlock(curBlock, nextBlock, cjc, tryCatch.getTryBlock());\n            for (CatchBlock cb : tryCatch.getCatchBlocks()) {\n                BasicBlock catchEntry = cjc.getEntry(cb);\n                if (catchEntry != nextBlock)\n                    buildBlock(catchEntry, nextBlock, jc, cb);\n            }\n        } else {\n            buildBlock(curBlock, nextBlock, jc, tryCatch.getTryBlock());\n        }\n    }\n\n    private void buildSwitch(final BasicBlock curBlock, BasicBlock nextBlock, JumpContext jc, Switch switchBlock) {\n        Expression condition = switchBlock.getCondition();\n        BasicBlock condBlock = buildExpr(curBlock, condition, jc);\n        jc = new SwitchJumpContext(jc, nextBlock);\n        List<CaseBlock> caseBlocks = switchBlock.getCaseBlocks();\n        BasicBlock finalCondBlock = nextBlock;\n        Map<Integer, BasicBlock> targets = new LinkedHashMap<>();\n        List<BasicBlock> blocks = new ArrayList<>();\n        CaseBlock prevBlock = null;\n        for (CaseBlock cb : caseBlocks) {\n            BasicBlock curBB;\n            if (prevBlock != null && prevBlock.isDefault() && prevBlock.getBody().isEmpty()) {\n                curBB = blocks.get(blocks.size() - 1);\n            } else {\n                curBB = new BasicBlock();\n            }\n            blocks.add(curBB);\n            if (cb.isDefault()) {\n                finalCondBlock = curBB;\n            } else {\n                for (Integer val : cb.getValues()) {\n                    targets.put(val, curBB);\n                }\n            }\n            if (!cb.getBody().isEmpty() && cb.getBody().get(0) instanceof Label) {\n                Label l = (Label) cb.getBody().get(0);\n                labelTargets.putIfAbsent(l, curBB);\n            }\n        }\n        blocks.add(nextBlock);\n        BasicBlock prevCondBlock = condBlock;\n        for (Entry<Integer, BasicBlock> entry : targets.entrySet()) {\n            Expression ldc = new Expression(AstCode.LdC, entry.getKey(), -1);\n            Expression eq = new Expression(AstCode.CmpEq, null, -1, condition, ldc);\n            BasicBlock curCondBlock = register(new BasicBlock(eq));\n            prevCondBlock.addTarget(prevCondBlock == condBlock ? EdgeType.PASS : EdgeType.FALSE, curCondBlock);\n            curCondBlock.addTarget(EdgeType.TRUE, entry.getValue());\n            prevCondBlock = curCondBlock;\n        }\n        prevCondBlock.addTarget(prevCondBlock == condBlock ? EdgeType.PASS : EdgeType.FALSE, finalCondBlock);\n        for (int i = 0; i < caseBlocks.size(); i++) {\n            CaseBlock caseBlock = caseBlocks.get(i);\n            BasicBlock caseBB = blocks.get(i);\n            BasicBlock nextBB = blocks.get(i + 1);\n            if (caseBlock.getBody().isEmpty()) {\n                // likely a procyon bug: seems that return is expected\n                buildNode(caseBB, nextBB, jc, new Expression(AstCode.Return, null, -1));\n            } else {\n                buildBlock(caseBB, nextBB, jc, caseBlock);\n            }\n        }\n    }\n\n    private void buildLoop(final BasicBlock curBlock, BasicBlock nextBlock, JumpContext jc, Loop loop)\n            throws InternalError {\n        Expression condition = loop.getCondition();\n        if (condition == null) {\n            jc = new LoopJumpContext(jc, curBlock, nextBlock, curBlock);\n            buildBlock(curBlock, curBlock, jc, loop.getBody());\n        } else if (loop.getLoopType() == LoopType.PreCondition) {\n            BasicBlock condExitBlock = buildExpr(curBlock, condition, jc);\n            if (loop.getBody().getBody().isEmpty()) {\n                condExitBlock.addTarget(EdgeType.TRUE, curBlock);\n                condExitBlock.addTarget(EdgeType.FALSE, nextBlock);\n            } else {\n                jc = new LoopJumpContext(jc, curBlock, nextBlock, curBlock);\n                BasicBlock bodyBlock = new BasicBlock();\n                condExitBlock.addTarget(EdgeType.TRUE, bodyBlock);\n                condExitBlock.addTarget(EdgeType.FALSE, nextBlock);\n                buildBlock(bodyBlock, curBlock, jc, loop.getBody());\n            }\n        } else if (loop.getLoopType() == LoopType.PostCondition) {\n            if (loop.getBody().getBody().isEmpty())\n                throw new InternalError(\"Unexpected empty do-while loop\");\n            BasicBlock condEntryBlock = new BasicBlock();\n            jc = new LoopJumpContext(jc, curBlock, nextBlock, condEntryBlock);\n            buildBlock(curBlock, condEntryBlock, jc, loop.getBody());\n            BasicBlock condExitBlock = buildExpr(condEntryBlock, condition, jc);\n            condExitBlock.addTarget(EdgeType.TRUE, curBlock);\n            condExitBlock.addTarget(EdgeType.FALSE, nextBlock);\n        } else\n            throw new InternalError(\"Unexpected loop type: \" + loop.getLoopType());\n    }\n\n    private void buildCondition(final BasicBlock curBlock, BasicBlock nextBlock, JumpContext jc, Condition condition) {\n        BasicBlock lastBlock = buildExpr(curBlock, condition.getCondition(), jc);\n        if (!condition.getTrueBlock().getBody().isEmpty()) {\n            BasicBlock leftBlock = new BasicBlock();\n            lastBlock.addTarget(EdgeType.TRUE, leftBlock);\n            buildBlock(leftBlock, nextBlock, jc, condition.getTrueBlock());\n        } else {\n            lastBlock.addTarget(EdgeType.TRUE, nextBlock);\n        }\n        if (!condition.getFalseBlock().getBody().isEmpty()) {\n            BasicBlock rightBlock = new BasicBlock();\n            lastBlock.addTarget(EdgeType.FALSE, rightBlock);\n            buildBlock(rightBlock, nextBlock, jc, condition.getFalseBlock());\n        } else {\n            lastBlock.addTarget(EdgeType.FALSE, nextBlock);\n        }\n    }\n\n    private BasicBlock buildExpr(BasicBlock entry, Expression expr, JumpContext jc) {\n        switch (expr.getCode()) {\n        case TernaryOp: {\n            Expression cond = expr.getArguments().get(0);\n            Expression left = expr.getArguments().get(1);\n            Expression right = expr.getArguments().get(2);\n            BasicBlock condBlock = buildExpr(entry, cond, jc);\n            BasicBlock leftBlock = new BasicBlock();\n            BasicBlock rightBlock = new BasicBlock();\n            BasicBlock ternary = new BasicBlock(expr);\n            condBlock.addTarget(EdgeType.TRUE, leftBlock);\n            condBlock.addTarget(EdgeType.FALSE, rightBlock);\n            buildExpr(leftBlock, left, jc).addTarget(EdgeType.PASS, ternary);\n            buildExpr(rightBlock, right, jc).addTarget(EdgeType.PASS, ternary);\n            return register(ternary);\n        }\n        case LogicalAnd: {\n            Expression left = expr.getArguments().get(0);\n            Expression right = expr.getArguments().get(1);\n            BasicBlock rightBlock = new BasicBlock();\n            BasicBlock and = new BasicBlock(expr);\n            BasicBlock leftRes = buildExpr(entry, left, jc);\n            leftRes.addTarget(EdgeType.TRUE, rightBlock);\n            leftRes.addTarget(EdgeType.FALSE, and);\n            BasicBlock rightRes = buildExpr(rightBlock, right, jc);\n            rightRes.addTarget(EdgeType.TRUE, and);\n            rightRes.addTarget(EdgeType.FALSE, and);\n            return register(and);\n        }\n        case LogicalOr: {\n            Expression left = expr.getArguments().get(0);\n            Expression right = expr.getArguments().get(1);\n            BasicBlock rightBlock = new BasicBlock();\n            BasicBlock or = new BasicBlock(expr);\n            BasicBlock leftBlock = buildExpr(entry, left, jc);\n            leftBlock.addTarget(EdgeType.FALSE, rightBlock);\n            leftBlock.addTarget(EdgeType.TRUE, or);\n            BasicBlock rightRes = buildExpr(rightBlock, right, jc);\n            rightRes.addTarget(EdgeType.TRUE, or);\n            rightRes.addTarget(EdgeType.FALSE, or);\n            return register(or);\n        }\n        default:\n            break;\n        }\n        BasicBlock block = entry;\n        for (Expression child : expr.getArguments()) {\n            BasicBlock lastExpr = buildExpr(block, child, jc);\n            block = new BasicBlock();\n            lastExpr.addTarget(EdgeType.PASS, block);\n        }\n        block.setExpression(expr);\n        register(block);\n        if (expr.getOperand() instanceof Lambda) {\n            Lambda lambda = (Lambda) expr.getOperand();\n            CFG lambdaCFG = new CFG(Nodes.getLambdaMethod(lambda), block, lambda.getBody());\n            lambdas.put(lambda, lambdaCFG);\n        }\n        switch (expr.getCode()) {\n        case AThrow: {\n            TypeReference exc = expr.getInferredType();\n            if (exc == null)\n                exc = expr.getExpectedType();\n            if (exc == null)\n                exc = throwable;\n            jc.addExceptional(block, exc);\n            return null;\n        }\n        case CheckCast:\n            jc.addExceptional(block, classCastException);\n            jc.addExceptional(block, linkageError);\n            break;\n        case InstanceOf:\n            jc.addExceptional(block, linkageError);\n            break;\n        case Goto:\n            jc.addJump(block, (Label) expr.getOperand());\n            return null;\n        case LoopContinue:\n            jc.addContinue(block, (Label) expr.getOperand());\n            return null;\n        case LoopOrSwitchBreak:\n            jc.addBreak(block, (Label) expr.getOperand());\n            return null;\n        case Return:\n            jc.addReturn(block);\n            return null;\n        case ArrayLength:\n            jc.addExceptional(block, nullPointerException);\n            break;\n        case Bind:\n        case InvokeDynamic:\n            jc.addExceptional(block, error);\n            jc.addExceptional(block, runtimeException);\n            break;\n        case InitObject:\n        case InvokeInterface:\n        case InvokeSpecial:\n        case InvokeStatic:\n        case InvokeVirtual: {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if(mr.getName().equals(\"exit\") && Types.is(mr.getDeclaringType(), System.class)) {\n                block.addTarget(EdgeType.PASS, implicit);\n                return null;\n            }\n            jc.addExceptional(block, error);\n            jc.addExceptional(block, runtimeException);\n            MethodDefinition md = mr.resolve();\n            if (md != null) {\n                for (TypeReference thrownType : md.getThrownTypes()) {\n                    jc.addExceptional(block, thrownType);\n                }\n            } else {\n                jc.addExceptional(block, exception);\n            }\n            break;\n        }\n        case LoadElement:\n            jc.addExceptional(block, arrayIndexOutOfBoundsException);\n            jc.addExceptional(block, nullPointerException);\n            break;\n        case StoreElement:\n            jc.addExceptional(block, arrayIndexOutOfBoundsException);\n            jc.addExceptional(block, arrayStoreException);\n            jc.addExceptional(block, nullPointerException);\n            break;\n        case __New:\n        case NewArray:\n        case InitArray:\n        case MultiANewArray:\n            jc.addExceptional(block, outOfMemoryError);\n            break;\n        case PutStatic:\n        case GetStatic: {\n            FieldReference fr = ((FieldReference) expr.getOperand());\n            if (!fr.getDeclaringType().isEquivalentTo(md.getDeclaringType()))\n                jc.addExceptional(block, linkageError);\n            break;\n        }\n        case PutField:\n        case GetField: {\n            if (md.isStatic() || !Exprs.isThis(expr.getArguments().get(0))) {\n                jc.addExceptional(block, nullPointerException);\n                jc.addExceptional(block, linkageError);\n            }\n            break;\n        }\n        default:\n        }\n        return block;\n    }\n\n    private BasicBlock register(BasicBlock block) {\n        block.setId(blocks.size());\n        blocks.add(block);\n        return block;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder sb = new StringBuilder(\"CFG for \").append(new MemberInfo(md)).append(\"\\n\");\n        for (BasicBlock bb : blocks)\n            sb.append(bb);\n        return sb.toString();\n    }\n\n    public static CFG build(MethodDefinition md, Block body) {\n        try {\n            return new CFG(md, null, body);\n        } catch (Exception e) {\n            throw new RuntimeException(\"Unable to build CFG for \" + new MemberInfo(md) + \"\\n\" + body, e);\n        }\n    }\n\n    public <STATE, FACT> boolean runDFA(Annotator<FACT> annotator,\n            BiFunction<MethodDefinition, STATE, Dataflow<FACT, STATE>> dfFactory, int maxIter) {\n        @SuppressWarnings(\"unchecked\")\n        STATE closureState = closure == null ? null : (STATE) closure.state;\n        boolean valid = new DFARunner<>(annotator, dfFactory.apply(md, closureState)).run(maxIter);\n        for (CFG subCFG : lambdas.values()) {\n            valid &= subCFG.runDFA(annotator, dfFactory, maxIter);\n        }\n        return valid;\n    }\n\n    public void forBodies(BiConsumer<MethodDefinition, Block> consumer) {\n        consumer.accept(md, body);\n        lambdas.values().forEach(cfg -> cfg.forBodies(consumer));\n    }\n\n    public CFG getLambdaCFG(Lambda lambda) {\n        return lambdas.get(lambda);\n    }\n    \n    private boolean isReachable(BasicBlock from, BasicBlock to) {\n        clearChanged();\n        boolean[] changed = { true };\n        from.changed = true;\n        while (changed[0]) {\n            changed[0] = false;\n            for (BasicBlock bb : blocks) {\n                if (bb.changed) {\n                    bb.targets().filter(t -> !t.changed).forEach(t -> {\n                        t.changed = changed[0] = true;\n                    });\n                }\n            }\n            if (to.changed)\n                return true;\n        }\n        return to.changed;\n    }\n    \n    private boolean isAlwaysReachable(BasicBlock from, Set<BasicBlock> targets) {\n        clearChanged();\n        boolean[] changed = { true };\n        from.changed = true;\n        while (changed[0]) {\n            changed[0] = false;\n            for (BasicBlock bb : blocks) {\n                if (targets.contains(bb))\n                    continue;\n                if (bb.changed) {\n                    // Explicit throw\n                    if(bb.passTarget == null && bb.trueTarget == null &&\n                            bb.targets().anyMatch(fail::equals))\n                        return false;\n                    bb.targets().filter(t -> !t.changed).forEach(t -> {\n                        t.changed = changed[0] = true;\n                    });\n                }\n            }\n            if (exit.changed || implicit.changed)\n                return false;\n        }\n        return true;\n    }\n    \n    private <T> boolean updateState(GraphSearch<T> gs, BasicBlock bb, T newState) {\n        @SuppressWarnings(\"unchecked\")\n        T oldState = (T) bb.state;\n        if(Objects.equals(oldState, newState))\n            return false;\n        newState = gs.merge(oldState, newState);\n        if(Objects.equals(oldState, newState))\n            return false;\n        bb.state = newState;\n        return bb.changed = true;\n    }\n\n    /**\n     * Result works only until new graph-search is performed.\n     * Must not be called during DFA run on the same CFG.\n     */\n    public <T> SearchResult<T> graphSearch(GraphSearch<T> gs) {\n        clearChanged();\n        for(BasicBlock bb : blocks) {\n            bb.state = gs.markStart(bb.expr, bb == entry);\n            if(bb.state != null)\n                bb.changed = true;\n        }\n        exit.state = fail.state = implicit.state = null;\n        boolean changed = true;\n        while(changed) {\n            changed = false;\n            for(BasicBlock bb : blocks) {\n                if(bb.changed) {\n                    bb.changed = false;\n                    @SuppressWarnings(\"unchecked\")\n                    T state = (T) bb.state;\n                    if(bb.passTarget != null) {\n                        changed |= updateState(gs, bb.passTarget, gs.transfer(state, bb.expr, EdgeType.PASS, bb.passTarget.expr));\n                    } else if(bb.trueTarget != null) {\n                        changed |= updateState(gs, bb.trueTarget, gs.transfer(state, bb.expr, EdgeType.TRUE, bb.trueTarget.expr));\n                        changed |= updateState(gs, bb.falseTarget, gs.transfer(state, bb.expr, EdgeType.FALSE, bb.falseTarget.expr));\n                    }\n                    if(bb.failTargets != null) {\n                        for(BasicBlock target : bb.failTargets)\n                            changed |= updateState(gs, target, gs.transfer(state, bb.expr, EdgeType.FAIL, target.expr));\n                    }\n                }\n            }\n            if(forwardTill == blocks.size())\n                break;\n        }\n        return new SearchResult<>(gs);\n    }\n    \n    @SuppressWarnings(\"unchecked\")\n    public class SearchResult<T> {\n        private final GraphSearch<T> gs;\n\n        SearchResult(GraphSearch<T> gs) {\n            this.gs = gs;\n        }\n        \n        public T atExit() {\n            return (T) exit.state;\n        }\n        \n        public T atFail() {\n            return (T) fail.state;\n        }\n        \n        public T atImplicit() {\n            return (T) implicit.state;\n        }\n        \n        public T atExpression(Expression expr) {\n            return blocksBy(expr).map(bb -> (T)bb.state).filter(Objects::nonNull).reduce(gs::merge).orElse(null);\n        }\n    }\n    \n    private Stream<BasicBlock> blocksBy(Expression expr) {\n        return blocks.stream().filter(bb -> bb.expr == expr);\n    }\n    \n    public boolean mayTerminateImplicitly(Expression expr) {\n        return blocksBy(expr).anyMatch(bb -> isReachable(bb, implicit));\n    }\n\n    public boolean isReachable(Expression expr) {\n        if (!hasUnreachable)\n            return true;\n        return blocksBy(expr).anyMatch(bb -> bb.reached);\n    }\n    \n    public boolean isAlwaysReachable(Expression from, Expression to) {\n        Set<BasicBlock> targets = blocksBy(to).collect(Collectors.toSet());\n        return blocksBy(from).allMatch(bb -> isAlwaysReachable(bb, targets));\n    }\n\n    public CodeBlock findDeadCode(Expression expr, EdgeType deadEdge) {\n        Set<BasicBlock> targetBlocks = blocksBy(expr).collect(Collectors.toSet());\n        if (targetBlocks.isEmpty())\n            return null;\n        if (deadEdge == EdgeType.TRUE || deadEdge == EdgeType.FALSE) {\n            if (targetBlocks.stream().allMatch(bb -> bb.trueTarget == null && bb.passTarget != null)) {\n                targetBlocks = targetBlocks.stream().map(bb -> bb.passTarget).collect(Collectors.toSet());\n                if (!targetBlocks.stream().allMatch(bb -> bb.expr != null && bb.expr.getCode() == AstCode.LogicalNot))\n                    return null;\n                deadEdge = deadEdge == EdgeType.TRUE ? EdgeType.FALSE : EdgeType.TRUE;\n            }\n        }\n        clearChanged();\n        boolean[] changed = { true };\n        entry.changed = true;\n        while (changed[0]) {\n            changed[0] = false;\n            for (BasicBlock bb : blocks) {\n                if (bb.changed) {\n                    Stream<BasicBlock> targets = targetBlocks.contains(bb) ? bb.targetsExcept(deadEdge) : bb.targets();\n                    targets.filter(t -> !t.changed).forEach(t -> {\n                        t.changed = changed[0] = true;\n                    });\n                }\n            }\n        }\n        BasicBlock deadCodeEntry = null;\n        Set<Expression> deadExpressions = new HashSet<>();\n        for (BasicBlock bb : blocks) {\n            if (!bb.changed && bb.reached) {\n                AstCode code = bb.expr.getCode();\n                if (code == AstCode.Goto || code == AstCode.LogicalAnd || code == AstCode.LogicalOr\n                    || code == AstCode.LoopContinue || code == AstCode.LoopOrSwitchBreak)\n                    continue;\n                deadExpressions.add(bb.expr);\n                if (deadCodeEntry == null) {\n                    deadCodeEntry = bb;\n                }\n            }\n        }\n        return deadCodeEntry == null ? null\n                : new CodeBlock(deadCodeEntry.expr, deadExpressions.size(), isExceptional(deadCodeEntry));\n    }\n    \n    public boolean isInCFG(Expression expr) {\n        return blocksBy(expr).findAny().isPresent();\n    }\n\n    private boolean isExceptional(BasicBlock start) {\n        return !isReachable(start, exit);\n    }\n\n    void clearChanged() {\n        for (BasicBlock bb : blocks) {\n            bb.changed = false;\n        }\n        exit.changed = false;\n        fail.changed = false;\n        implicit.changed = false;\n    }\n\n    void initialize() {\n        for (BasicBlock bb : blocks) {\n            bb.state = null;\n        }\n        exit.state = null;\n        fail.state = null;\n        implicit.state = null;\n    }\n    \n    class DFARunner<STATE, FACT> {\n        private final Annotator<FACT> annotator;\n        private final Dataflow<FACT, STATE> df;\n        private boolean changed = false;\n\n        DFARunner(Annotator<FACT> annotator, Dataflow<FACT, STATE> df) {\n            this.df = df;\n            this.annotator = annotator;\n        }\n\n        boolean run(int maxIteration) {\n            if (blocks.isEmpty()) {\n                return true;\n            }\n            initialize();\n            entry.state = df.makeEntryState();\n            runIteration(blocks);\n            boolean valid = true;\n            if (changed && forwardTill < blocks.size()) {\n                List<BasicBlock> subList = blocks.subList(forwardTill, blocks.size());\n                valid = false;\n                for (int iter = 0; iter < maxIteration; iter++) {\n                    runIteration(subList);\n                    if (!changed) {\n                        valid = true;\n                        break;\n                    }\n                }\n                if (!valid) {\n                    for (BasicBlock bb : subList) {\n                        if (bb.changed) {\n                            annotator.put(bb.expr, df.makeUnknownFact());\n                            bb.state = null;\n                        }\n                    }\n                    if (exit.changed) {\n                        exit.state = null;\n                    }\n                    if (fail.changed) {\n                        fail.state = null;\n                    }\n                }\n            }\n            for (List<BasicBlock> dupList : dupExpr) {\n                FACT res = dupList.stream().map(bb -> annotator.get(bb.expr)).reduce(null, df::mergeFacts);\n                dupList.forEach(bb -> annotator.put(bb.expr, res));\n            }\n            @SuppressWarnings(\"unchecked\")\n            STATE exitState = (STATE) exit.state;\n            df.onSuccess(exitState);\n            @SuppressWarnings(\"unchecked\")\n            STATE failState = (STATE) fail.state;\n            df.onFail(failState);\n            return valid;\n        }\n\n        private void runIteration(List<BasicBlock> blocks) {\n            changed = false;\n            clearChanged();\n            for (BasicBlock bb : blocks) {\n                try {\n                    if (!bb.reached) {\n                        annotator.put(bb.expr, df.makeUnknownFact());\n                        continue;\n                    }\n                    @SuppressWarnings(\"unchecked\")\n                    STATE state = (STATE) bb.state;\n                    FACT fact = df.makeFact(state, bb.expr);\n                    FACT oldFact = annotator.get(bb.expr);\n                    if (!df.sameFact(oldFact, fact)) {\n                        FACT updatedFact = df.mergeFacts(oldFact, fact);\n                        if (!df.sameFact(updatedFact, oldFact)) {\n                            annotator.put(bb.expr, updatedFact);\n                            bb.changed = changed = true;\n                        }\n                    }\n                    if (bb.expr.getCode() == AstCode.Goto) {\n                        updateState(state, bb.passTarget);\n                        continue;\n                    }\n                    if (bb.passTarget != null) {\n                        updateState(df.transferState(state, bb.expr), bb.passTarget);\n                    }\n                    if (bb.trueTarget != null || bb.falseTarget != null) {\n                        TrueFalse<STATE> tf = transferConditional(bb.expr, state);\n                        updateState(tf.trueState, bb.trueTarget);\n                        updateState(tf.falseState, bb.falseTarget);\n                    }\n                    if (bb.failTargets != null) {\n                        STATE newState = bb.expr.getCode() == AstCode.Ret ? df.transferState(state, bb.expr)\n                                : df.transferExceptionalState(state, bb.expr);\n                        for (BasicBlock target : bb.failTargets) {\n                            updateState(newState, target);\n                        }\n                    }\n                } catch (Exception e) {\n                    throw new RuntimeException(\"Error running DFA at block \" + bb + \"\\n\" + CFG.this + CFG.this.body, e);\n                }\n            }\n        }\n\n        private TrueFalse<STATE> transferConditional(Expression expr, STATE state) {\n            boolean invert = false;\n            while(expr.getCode() == AstCode.LogicalNot) {\n                invert = !invert;\n                expr = expr.getArguments().get(0);\n            }\n            TrueFalse<STATE> tf = df.transferConditionalState(state, expr);\n            return invert ? tf.invert() : tf;\n        }\n\n        private void updateState(STATE newState, BasicBlock target) {\n            @SuppressWarnings(\"unchecked\")\n            STATE oldState = (STATE) target.state;\n            if (oldState == null) {\n                if (newState != null) {\n                    target.state = newState;\n                    target.changed = changed = true;\n                }\n            } else if (newState != null && !df.sameState(oldState, newState)) {\n                STATE updatedState = df.mergeStates(oldState, newState);\n                target.state = updatedState;\n                if (!df.sameState(oldState, updatedState)) {\n                    target.changed = changed = true;\n                }\n            }\n        }\n\n        @Override\n        public String toString() {\n            StringBuilder sb = new StringBuilder();\n            for (BasicBlock bb : blocks) {\n                sb.append(getBlockDescription(bb));\n            }\n            sb.append(getBlockDescription(exit));\n            sb.append(getBlockDescription(fail));\n            return sb.toString();\n        }\n\n        String getBlockDescription(BasicBlock bb) {\n            return \"[\" + bb.getId() + \"] \" + (bb.changed ? \"*\" : \" \") + \" \" + bb.state + \" | \" + (bb.expr == null ? \"?\"\n                    : annotator.get(bb.expr)) + \"\\n\";\n        }\n    }\n\n    public enum EdgeType {\n        PASS, TRUE, FALSE, FAIL;\n    }\n\n    static class BasicBlock {\n        Object state;\n        boolean changed, reached, synthetic;\n        int id = -1;\n        Expression expr;\n        BasicBlock passTarget;\n        BasicBlock trueTarget;\n        BasicBlock falseTarget;\n        List<BasicBlock> failTargets;\n\n        BasicBlock() {\n        }\n\n        BasicBlock(int id) {\n            this.id = id;\n        }\n\n        BasicBlock(Expression expr) {\n            this.expr = expr;\n        }\n\n        BasicBlock(boolean synthetic, Expression expr) {\n            this.expr = expr;\n            this.synthetic = synthetic;\n        }\n        \n        void setId(int id) {\n            if (this.id >= 0)\n                throw new IllegalStateException(\"Double id: \" + this.id + \"/\" + id);\n            this.id = id;\n        }\n        \n        BasicBlock falseOrPass() {\n            return falseTarget != null ? falseTarget : passTarget;\n        }\n\n        BasicBlock trueOrPass() {\n            return trueTarget != null ? trueTarget : passTarget;\n        }\n\n        void setExpression(Expression expr) {\n            if (this.expr != null)\n                throw new IllegalStateException(\"Expression is set twice: \" + expr + \"\\n\" + this);\n            this.expr = expr;\n        }\n\n        void addTarget(EdgeType type, BasicBlock target) {\n            switch (type) {\n            case FAIL:\n                if (failTargets == null)\n                    failTargets = new ArrayList<>();\n                if (!failTargets.contains(target))\n                    failTargets.add(target);\n                break;\n            case FALSE:\n                if (falseTarget != null || passTarget != null)\n                    throw new IllegalStateException(\"False target is set twice: \" + expr);\n                falseTarget = target;\n                break;\n            case PASS:\n                if (passTarget != null || trueTarget != null || falseTarget != null)\n                    throw new IllegalStateException(\"Pass target is set twice: \" + expr);\n                passTarget = target;\n                break;\n            case TRUE:\n                if (trueTarget != null || passTarget != null)\n                    throw new IllegalStateException(\"True target is set twice: \" + expr);\n                trueTarget = target;\n                break;\n            default:\n                throw new InternalError();\n            }\n        }\n\n        public String getId() {\n            switch (id) {\n            case BLOCKTYPE_UNKNOWN:\n                return \"UNKNOWN\";\n            case BLOCKTYPE_EXIT:\n                return \"EXIT\";\n            case BLOCKTYPE_FAIL:\n                return \"FAIL\";\n            case BLOCKTYPE_IMPLICIT:\n                return \"IMPLICIT\";\n            default:\n                return String.valueOf(id);\n            }\n        }\n\n        public Stream<BasicBlock> targets() {\n            Stream<BasicBlock> stream = Stream.of(passTarget, trueTarget, falseTarget);\n            if (failTargets != null)\n                stream = Stream.concat(stream, failTargets.stream());\n            return stream.filter(Objects::nonNull);\n        }\n\n        public Stream<BasicBlock> targetsExcept(EdgeType type) {\n            switch (type) {\n            case PASS: {\n                Stream<BasicBlock> stream = Stream.of(trueTarget, falseTarget);\n                if (failTargets != null)\n                    stream = Stream.concat(stream, failTargets.stream());\n                return stream.filter(Objects::nonNull);\n            }\n            case TRUE: {\n                Stream<BasicBlock> stream = Stream.of(passTarget, falseTarget);\n                if (failTargets != null)\n                    stream = Stream.concat(stream, failTargets.stream());\n                return stream.filter(Objects::nonNull);\n            }\n            case FALSE: {\n                Stream<BasicBlock> stream = Stream.of(passTarget, trueTarget);\n                if (failTargets != null)\n                    stream = Stream.concat(stream, failTargets.stream());\n                return stream.filter(Objects::nonNull);\n            }\n            case FAIL:\n                return Stream.of(passTarget, trueTarget, falseTarget).filter(Objects::nonNull);\n            default:\n                throw new InternalError();\n            }\n        }\n\n        @Override\n        public String toString() {\n            StringBuilder sb = new StringBuilder(\"[\").append(getId()).append(\"]\").append(!reached ? \"-\" : \" \").append(\n                expr).append(\"\\n  \");\n            if (passTarget != null)\n                sb.append(\"PASS -> [\").append(passTarget.getId()).append(\"] \");\n            if (trueTarget != null)\n                sb.append(\"TRUE -> [\").append(trueTarget.getId()).append(\"] \");\n            if (falseTarget != null)\n                sb.append(\"FALSE -> [\").append(falseTarget.getId()).append(\"] \");\n            if (failTargets != null)\n                sb.append(\"FAIL -> [\").append(failTargets.stream().map(BasicBlock::getId).collect(Collectors.joining(\n                    \",\"))).append(\"]\");\n            sb.append(\"\\n\");\n            return sb.toString();\n        }\n    }\n\n    interface JumpContext {\n        void addReturn(BasicBlock block);\n\n        void addBreak(BasicBlock block, Label label);\n\n        void addContinue(BasicBlock block, Label label);\n\n        void addJump(BasicBlock block, Label label);\n\n        void addExceptional(BasicBlock block, TypeReference exception);\n    }\n\n    class OuterJumpContext implements JumpContext {\n        @Override\n        public void addExceptional(BasicBlock block, TypeReference exception) {\n            block.addTarget(EdgeType.FAIL, fail);\n        }\n\n        @Override\n        public void addReturn(BasicBlock block) {\n            block.addTarget(EdgeType.PASS, exit);\n        }\n\n        @Override\n        public void addBreak(BasicBlock block, Label label) {\n            if (label != null)\n                addJump(block, label);\n            else\n                throw new IllegalStateException(\"Misplaced break\");\n        }\n\n        @Override\n        public void addContinue(BasicBlock block, Label label) {\n            throw new IllegalStateException(\"Misplaced continue\");\n        }\n\n        @Override\n        public void addJump(BasicBlock block, Label label) {\n            block.addTarget(EdgeType.PASS, labelTargets.computeIfAbsent(label, k -> new BasicBlock()));\n        }\n    }\n\n    static abstract class DelegatingJumpContext implements JumpContext {\n        private final JumpContext parent;\n\n        protected DelegatingJumpContext(JumpContext parent) {\n            this.parent = parent;\n        }\n\n        @Override\n        public void addReturn(BasicBlock block) {\n            parent.addReturn(block);\n        }\n\n        @Override\n        public void addBreak(BasicBlock block, Label label) {\n            parent.addBreak(block, label);\n        }\n\n        @Override\n        public void addContinue(BasicBlock block, Label label) {\n            parent.addContinue(block, label);\n        }\n\n        @Override\n        public void addJump(BasicBlock block, Label label) {\n            parent.addJump(block, label);\n        }\n\n        @Override\n        public void addExceptional(BasicBlock block, TypeReference exception) {\n            parent.addExceptional(block, exception);\n        }\n    }\n\n    static class SwitchJumpContext extends DelegatingJumpContext {\n        private final BasicBlock exit;\n\n        public SwitchJumpContext(JumpContext parent, BasicBlock exit) {\n            super(parent);\n            this.exit = exit;\n        }\n\n        @Override\n        public void addBreak(BasicBlock block, Label label) {\n            if (label == null) {\n                block.addTarget(EdgeType.PASS, exit);\n            } else {\n                super.addBreak(block, label);\n            }\n        }\n    }\n\n    class LoopJumpContext extends DelegatingJumpContext {\n        private final BasicBlock exit;\n        private final BasicBlock continueTarget;\n        private final BasicBlock entry;\n\n        public LoopJumpContext(JumpContext parent, BasicBlock entry, BasicBlock exit, BasicBlock continueTarget) {\n            super(parent);\n            this.entry = entry;\n            this.exit = exit;\n            this.continueTarget = continueTarget;\n        }\n\n        @Override\n        public void addBreak(BasicBlock block, Label label) {\n            if (label == null) {\n                block.addTarget(EdgeType.PASS, exit);\n            } else {\n                super.addBreak(block, label);\n            }\n        }\n\n        @Override\n        public void addContinue(BasicBlock block, Label label) {\n            if (label == null || labelTargets.get(label) == entry) {\n                block.addTarget(EdgeType.PASS, continueTarget);\n            } else {\n                super.addBreak(block, label);\n            }\n        }\n    }\n\n    class LabelJumpContext extends DelegatingJumpContext {\n        private final Set<Label> labels;\n\n        LabelJumpContext(JumpContext parent, Set<Label> labels) {\n            super(parent);\n            this.labels = labels;\n        }\n\n        @Override\n        public void addBreak(BasicBlock block, Label label) {\n            if (label != null) {\n                addJump(block, label);\n            } else {\n                super.addBreak(block, label);\n            }\n        }\n\n        @Override\n        public void addJump(BasicBlock block, Label label) {\n            if (labels.contains(label)) {\n                block.addTarget(EdgeType.PASS, labelTargets.computeIfAbsent(label, k -> new BasicBlock()));\n            } else {\n                super.addJump(block, label);\n            }\n        }\n    }\n\n    static class CatchJumpContext extends DelegatingJumpContext {\n        private final Map<CatchBlock, BasicBlock> catches;\n\n        CatchJumpContext(JumpContext parent, BasicBlock nextBlock, List<CatchBlock> catchBlocks) {\n            super(parent);\n            this.catches = catchBlocks.stream().collect(Collectors.toMap(Function.identity(), cb -> cb.getBody()\n                    .isEmpty() ? nextBlock : new BasicBlock(), (a, b) -> a, LinkedHashMap::new));\n        }\n\n        @Override\n        public void addExceptional(BasicBlock block, TypeReference exception) {\n            for (Entry<CatchBlock, BasicBlock> entry : catches.entrySet()) {\n                for (TypeReference tr : types(entry.getKey())) {\n                    if (Types.isInstance(exception, tr)) {\n                        // Exact match: catch and return\n                        block.addTarget(EdgeType.FAIL, entry.getValue());\n                        return;\n                    } else if (Types.isInstance(tr, exception) || !Types.hasCompleteHierarchy(tr.resolve())) {\n                        // Inexact match: probably caught here or by another catch block\n                        block.addTarget(EdgeType.FAIL, entry.getValue());\n                    }\n                }\n            }\n            super.addExceptional(block, exception);\n        }\n\n        BasicBlock getEntry(CatchBlock cb) {\n            return catches.get(cb);\n        }\n\n        private static Collection<TypeReference> types(CatchBlock cb) {\n            if (cb.getCaughtTypes().isEmpty())\n                return Collections.singleton(cb.getExceptionType());\n            return cb.getCaughtTypes();\n        }\n    }\n\n    static class FinallyJumpContext extends DelegatingJumpContext {\n        final Map<BasicBlock, BasicBlock> targetEntries = new HashMap<>();\n\n        FinallyJumpContext(JumpContext parent) {\n            super(parent);\n        }\n\n        private void replacePass(BasicBlock block) {\n            if (block.passTarget == null)\n                throw new IllegalStateException(\"Passtarget is null for \" + block);\n            block.passTarget = targetEntries.computeIfAbsent(block.passTarget, t -> new BasicBlock());\n        }\n\n        @Override\n        public void addReturn(BasicBlock block) {\n            super.addReturn(block);\n            replacePass(block);\n        }\n\n        @Override\n        public void addBreak(BasicBlock block, Label label) {\n            super.addBreak(block, label);\n            replacePass(block);\n        }\n\n        @Override\n        public void addContinue(BasicBlock block, Label label) {\n            super.addContinue(block, label);\n            replacePass(block);\n        }\n\n        @Override\n        public void addJump(BasicBlock block, Label label) {\n            super.addJump(block, label);\n            replacePass(block);\n        }\n\n        @Override\n        public void addExceptional(BasicBlock block, TypeReference exception) {\n            List<BasicBlock> oldTargets = block.failTargets;\n            block.failTargets = null;\n            super.addExceptional(block, exception);\n            List<BasicBlock> newTargets = block.failTargets;\n            block.failTargets = oldTargets;\n            newTargets.forEach(t -> block.addTarget(EdgeType.FAIL, targetEntries.computeIfAbsent(t,\n                tt -> new BasicBlock())));\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/ClassFields.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport one.util.huntbugs.db.FieldStats;\nimport one.util.huntbugs.db.MethodStats;\nimport one.util.huntbugs.db.MethodStats.MethodData;\nimport one.util.huntbugs.flow.SourceAnnotator.Frame;\nimport one.util.huntbugs.util.Annotations;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.decompiler.ast.Expression;\n\n/**\n * @author lan\n *\n */\npublic class ClassFields {\n    Map<MemberInfo, FieldDefinition> fields = new HashMap<>();\n    Map<MemberInfo, Expression> values = new HashMap<>();\n    Set<FieldDefinition> initializedInCtor = new HashSet<>();\n    MethodStats ms;\n    Map<MemberInfo, Map<MemberInfo, Expression>> ctorFields = new HashMap<>();\n    \n    public ClassFields(TypeDefinition td, FieldStats fieldStats, MethodStats methodStats) {\n        this.ms = methodStats;\n        for (FieldDefinition fd : td.getDeclaredFields()) {\n            fields.put(new MemberInfo(fd), fd);\n            int flags = fieldStats.getFlags(fd);\n            if(Flags.testAny(flags, FieldStats.WRITE_CONSTRUCTOR) &&\n                    !Flags.testAny(flags, FieldStats.WRITE_CLASS | FieldStats.WRITE_PACKAGE | FieldStats.WRITE_OUTSIDE) &&\n                    !Annotations.hasAnnotation(fd, true)) {\n                initializedInCtor.add(fd);\n            }\n        }\n    }\n    \n    public boolean isSideEffectFree(MethodReference mr, boolean exact) {\n        if(Methods.isSideEffectFree(mr))\n            return true;\n        MethodData stats = ms.getStats(mr);\n        if(stats == null)\n            return false;\n        return !stats.mayHaveSideEffect(exact);\n    }\n\n    public boolean isKnownFinal(MemberInfo field) {\n        FieldDefinition fd = fields.get(field);\n        return fd != null && fd.isFinal();\n    }\n\n    public boolean isKnownEffectivelyFinal(MemberInfo field) {\n        FieldDefinition fd = fields.get(field);\n        return fd != null && (fd.isFinal() || initializedInCtor.contains(fd));\n    }\n    \n    void mergeConstructor(MethodDefinition md, Frame frame, FrameContext fc) {\n        ctorFields.put(new MemberInfo(md), frame.fieldValues);\n        frame.fieldValues.forEach((mi, expr) -> {\n            FieldDefinition fd = fields.get(mi);\n            if (fd != null && !fd.isStatic() && (fd.isFinal() || (fd.isPrivate() || fd.isPackagePrivate())\n                    && initializedInCtor.contains(fd))) {\n                // TODO: better merging\n                values.merge(mi, expr, (e1, e2) -> SourceAnnotator.makePhiNode(e1, e2, fc));\n            }\n        });\n    }\n    \n    void setStaticFinalFields(Frame frame) {\n        frame.fieldValues.forEach((mi, expr) -> {\n            FieldDefinition fd = fields.get(mi);\n            if(fd != null && fd.isStatic() && (fd.isFinal() || (fd.isPrivate() || fd.isPackagePrivate())\n                    && initializedInCtor.contains(fd))) {\n                values.put(mi, expr);\n            }\n        });\n    }\n    \n    public void clearCtorData() {\n        ctorFields = null;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/CodeBlock.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport com.strobel.decompiler.ast.Expression;\n\npublic class CodeBlock {\n    public final Expression startExpr;\n    public final int length;\n    public final boolean isExceptional;\n\n    public CodeBlock(Expression startExpr, int length, boolean isExceptional) {\n        this.startExpr = startExpr;\n        this.length = length;\n        this.isExceptional = isExceptional;\n    }\n}"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/ConstAnnotator.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class ConstAnnotator extends Annotator<Object> {\n    static final Object UNKNOWN_VALUE = new Object() {\n        @Override\n        public String toString() {\n            return \"??\";\n        }\n    };\n\n    ConstAnnotator() {\n        super(\"value\", null);\n    }\n    \n    boolean build(CFG cfg) {\n        return cfg.<ContextValues, Object> runDFA(this, (md, closureState) -> new ConstDataflow(closureState == null\n                ? ContextValues.DEFAULT : closureState), 7);\n    }\n    \n    /**\n     * Returns statically known constant for given expression\n     * \n     * @param input expression to text\n     * @return statically known constant value (if any) or null (if value is unknown)\n     * Possible return types are:\n     * - boxed primitives\n     * - String\n     * - TypeReference (when constant is class literal)\n     * - EnumConstant (which refers to enum type and enum constant name)\n     * Note that sometimes boolean is returned as Integer (0 = false, 1 = true)\n     */\n    public Object getValue(Expression input) {\n        Object value = get(input);\n        return value == UNKNOWN_VALUE ? null : value;\n    }\n    \n    public boolean isConst(Expression input, Object constant) {\n        return constant.equals(get(input));\n    }\n    \n    static JvmType getType(Expression expr) {\n        TypeReference type = expr.getInferredType();\n        return type == null ? JvmType.Void : type.getSimpleType();\n    }\n\n    static final class ContextValues {\n        static final ContextValues DEFAULT = new ContextValues(null);\n        \n        final Map<Variable, Object> values;\n        \n        private ContextValues(Map<Variable, Object> values) {\n            this.values = values;\n        }\n        \n        ContextValues merge(ContextValues other) {\n            if(this == other)\n                return this;\n            if(this == DEFAULT || other == DEFAULT)\n                return DEFAULT;\n            Map<Variable, Object> newValues = new HashMap<>(values);\n            newValues.keySet().retainAll(other.values.keySet());\n            if(newValues.isEmpty())\n                return DEFAULT;\n            other.values.forEach((k, v) -> newValues.compute(k, (oldK, oldV) -> Objects.equals(v, oldV) ? v : null));\n            return newValues.isEmpty() ? DEFAULT : new ContextValues(newValues);\n        }\n        \n        ContextValues add(Variable var, Object value) {\n            if(values == null) {\n                return new ContextValues(Collections.singletonMap(var, value));\n            }\n            if(Objects.equals(value, values.get(var)))\n                return this;\n            Map<Variable, Object> newValues = new HashMap<>(values);\n            newValues.put(var, value);\n            return new ContextValues(newValues);\n        }\n        \n        ContextValues remove(Variable var) {\n            if(values != null && values.containsKey(var)) {\n                if(values.size() == 1)\n                    return DEFAULT;\n                Map<Variable, Object> newValues = new HashMap<>(values);\n                newValues.remove(var);\n                return new ContextValues(newValues);\n            }\n            return this;\n        }\n        \n        ContextValues transfer(Expression expr) {\n            Variable var = Nodes.getWrittenVariable(expr);\n            return var == null ? this : remove(var);\n        }\n        \n        Object resolve(Expression expr) {\n            Object oper = expr.getOperand();\n            Object result = oper instanceof Variable && values != null ? values.get(oper) : null;\n            return result == UNKNOWN_VALUE ? null : result;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (obj == null || getClass() != obj.getClass())\n                return false;\n            ContextValues other = (ContextValues) obj;\n            return Objects.equals(values, other.values);\n        }\n        \n        @Override\n        public String toString() {\n            return values == null ? \"{}\" : values.toString();\n        }\n    }\n    \n    class ConstDataflow implements Dataflow<Object, ContextValues> {\n        ContextValues initState;\n        \n        ConstDataflow(ContextValues initState) {\n            this.initState = initState;\n        }\n        \n        private Object resolve(ContextValues contextValues, Expression expr) {\n            if(expr.getCode() == AstCode.LdC) {\n                return expr.getOperand() == null ? UNKNOWN_VALUE : expr.getOperand();\n            }\n            Object val = get(expr);\n            if(val instanceof Exceptional) {\n                return UNKNOWN_VALUE;\n            }\n            if(val != UNKNOWN_VALUE && val != null) {\n                return val;\n            }\n            Object resolved = contextValues.resolve(expr);\n            if(resolved != null)\n                return resolved;\n            return val;\n        }\n\n        private Object resolveConditional(ContextValues contextValues, Expression expr) {\n            Object val = resolve(contextValues, expr);\n            // Zero is excluded as 0.0 == -0.0\n            if (val instanceof Double && ((double)val == 0.0 || Double.isNaN((double) val)))\n                return UNKNOWN_VALUE;\n            if (val instanceof Float && ((float)val == 0.0 || Float.isNaN((float) val)))\n                return UNKNOWN_VALUE;\n            return val;\n        }\n\n        private Object fromSource(ContextValues ctx, Expression expr) {\n            Object value = ctx.resolve(expr);\n            if(value != null)\n                return value;\n            Expression src = ValuesFlow.getSource(expr);\n            if(src == expr)\n                return UNKNOWN_VALUE;\n            value = resolve(ctx, src);\n            if(value != null)\n                return value;\n            if(src.getCode() == SourceAnnotator.PHI_TYPE) {\n                for(Expression child : src.getArguments()) {\n                    Object newVal = resolve(ctx, child);\n                    if (newVal == null) {\n                        if (Exprs.isParameter(child) || child.getCode() == SourceAnnotator.UPDATE_TYPE) {\n                            return UNKNOWN_VALUE;\n                        }\n                    } else if (value == null) {\n                        value = newVal;\n                    } else if (!value.equals(newVal)) {\n                        return UNKNOWN_VALUE;\n                    }\n                }\n                return value;\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Integer getArrayLength(Expression expression) {\n            return ValuesFlow.reduce(expression, e -> {\n                switch(e.getCode()) {\n                case InvokeVirtual: {\n                    MethodReference mr = (MethodReference) e.getOperand();\n                    if (mr.getName().equals(\"clone\") && mr.getErasedSignature().startsWith(\"()\")) {\n                        return getArrayLength(Exprs.getChild(e, 0));\n                    }\n                    return null;\n                }\n                case CheckCast:\n                    return getArrayLength(Exprs.getChild(e, 0));\n                case InitArray:\n                    return e.getArguments().size();\n                case NewArray:\n                    Object constant = getValue(e.getArguments().get(0));\n                    if(constant instanceof Integer)\n                        return (Integer)constant;\n                    return null;\n                default:\n                    return null;\n                }\n            }, (a, b) -> Objects.equals(a, b) ? a : null, Objects::isNull);\n        }\n\n        private Object processNeg(Expression expr) {\n            switch (getType(expr)) {\n            case Integer:\n                return processUnaryOp(expr, Integer.class, l -> -l);\n            case Long:\n                return processUnaryOp(expr, Long.class, l -> -l);\n            case Double:\n                return processUnaryOp(expr, Double.class, l -> -l);\n            case Float:\n                return processUnaryOp(expr, Float.class, l -> -l);\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processRem(Expression expr) {\n            switch (getType(expr)) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a % b);\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a % b);\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a % b);\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a % b);\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processDiv(Expression expr) {\n            switch (getType(expr)) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a / b);\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a / b);\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a / b);\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a / b);\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processMul(Expression expr) {\n            switch (getType(expr)) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a * b);\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a * b);\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a * b);\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a * b);\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processSub(Expression expr) {\n            switch (getType(expr)) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a - b);\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a - b);\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a - b);\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a - b);\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processAdd(Expression expr) {\n            switch (getType(expr)) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, Integer::sum);\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, Long::sum);\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, Double::sum);\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, Float::sum);\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processCmpGe(Expression expr) {\n            switch (getType(expr.getArguments().get(0))) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a.intValue() >= b.intValue());\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a.longValue() >= b.longValue());\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a.doubleValue() >= b\n                        .doubleValue());\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a.floatValue() >= b.floatValue());\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processCmpGt(Expression expr) {\n            switch (getType(expr.getArguments().get(0))) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a.intValue() > b.intValue());\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a.longValue() > b.longValue());\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a.doubleValue() > b.doubleValue());\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a.floatValue() > b.floatValue());\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processCmpLe(Expression expr) {\n            switch (getType(expr.getArguments().get(0))) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a.intValue() <= b.intValue());\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a.longValue() <= b.longValue());\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a.doubleValue() <= b\n                        .doubleValue());\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a.floatValue() <= b.floatValue());\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processCmpLt(Expression expr) {\n            switch (getType(expr.getArguments().get(0))) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a.intValue() < b.intValue());\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a.longValue() < b.longValue());\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a.doubleValue() < b.doubleValue());\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a.floatValue() < b.floatValue());\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processCmpNe(Expression expr) {\n            switch (getType(expr.getArguments().get(0))) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a.intValue() != b.intValue());\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a.longValue() != b.longValue());\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a.doubleValue() != b\n                        .doubleValue());\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a.floatValue() != b.floatValue());\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processCmpEq(Expression expr) {\n            switch (getType(expr.getArguments().get(0))) {\n            case Byte:\n            case Short:\n            case Character:\n            case Integer:\n                return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a.intValue() == b.intValue());\n            case Long:\n                return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a.longValue() == b.longValue());\n            case Double:\n                return processBinaryOp(expr, Double.class, Double.class, (a, b) -> a.doubleValue() == b\n                        .doubleValue());\n            case Float:\n                return processBinaryOp(expr, Float.class, Float.class, (a, b) -> a.floatValue() == b.floatValue());\n            default:\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private Object processKnownMethods(Expression expr, MethodReference mr) {\n            if (Methods.isEqualsMethod(mr)) {\n                return processBinaryOp(expr, Object.class, Object.class, Object::equals);\n            } else if (mr.getDeclaringType().getInternalName().equals(\"java/lang/String\")) {\n                if (mr.getName().equals(\"length\"))\n                    return processUnaryOp(expr, String.class, String::length);\n                else if (mr.getName().equals(\"toString\") || mr.getName().equals(\"intern\"))\n                    return processUnaryOp(expr, String.class, Function.identity());\n                else if (mr.getName().equals(\"trim\"))\n                    return processUnaryOp(expr, String.class, String::trim);\n                else if (mr.getName().equals(\"substring\"))\n                    return processBinaryOp(expr, String.class, Integer.class, String::substring);\n                else if (mr.getName().equals(\"valueOf\") && mr.getParameters().size() == 1) {\n                    if(mr.getErasedSignature().startsWith(\"(Z)\")) {\n                        // Handle specially to process possible Integer -> Boolean conversion\n                        return processUnaryOp(expr, Boolean.class, String::valueOf);\n                    }\n                    return processUnaryOp(expr, Object.class, String::valueOf);\n                }\n            } else if (mr.getDeclaringType().getInternalName().equals(\"java/lang/Math\")) {\n                if (mr.getName().equals(\"abs\")) {\n                    switch (getType(expr)) {\n                    case Integer:\n                        return processUnaryOp(expr, Integer.class, Math::abs);\n                    case Long:\n                        return processUnaryOp(expr, Long.class, Math::abs);\n                    case Double:\n                        return processUnaryOp(expr, Double.class, Math::abs);\n                    case Float:\n                        return processUnaryOp(expr, Float.class, Math::abs);\n                    default:\n                    }\n                }\n            } else if (Nodes.isBoxing(expr) || Nodes.isUnboxing(expr)) {\n                return processUnaryOp(expr, Number.class, Function.identity());\n            } else if (mr.getName().equals(\"toString\") && mr.getDeclaringType().getInternalName().startsWith(\"java/lang/\")\n                && expr.getArguments().size() == 1) {\n                if(mr.getDeclaringType().getInternalName().equals(\"java/lang/Boolean\")) {\n                    return processUnaryOp(expr, Boolean.class, Object::toString);\n                }\n                return processUnaryOp(expr, Object.class, Object::toString);\n            } else if (expr.getCode() == AstCode.InvokeStatic && expr.getArguments().size() == 1) {\n                if(mr.getName().equals(\"parseInt\") && mr.getDeclaringType().getInternalName().equals(\"java/lang/Integer\")) {\n                    return processUnaryOp(expr, String.class, Integer::parseInt);\n                } else if(mr.getName().equals(\"parseLong\") && mr.getDeclaringType().getInternalName().equals(\"java/lang/Long\")) {\n                    return processUnaryOp(expr, String.class, Long::parseLong);\n                } else if(mr.getName().equals(\"parseDouble\") && mr.getDeclaringType().getInternalName().equals(\"java/lang/Double\")) {\n                    return processUnaryOp(expr, String.class, Double::parseDouble);\n                } else if(mr.getName().equals(\"parseFloat\") && mr.getDeclaringType().getInternalName().equals(\"java/lang/Float\")) {\n                    return processUnaryOp(expr, String.class, Float::parseFloat);\n                }\n            }\n            return UNKNOWN_VALUE;\n        }\n\n        private <A> Object processUnaryOp(Expression expr, Class<A> type, Function<A, ?> op) {\n            if (expr.getArguments().size() != 1)\n                return UNKNOWN_VALUE;\n            Object arg = get(expr.getArguments().get(0));\n            if (arg == UNKNOWN_VALUE) {\n                return UNKNOWN_VALUE;\n            }\n            if (!type.isInstance(arg)) {\n                if(type == Boolean.class && arg instanceof Integer)\n                    arg = Integer.valueOf(1).equals(arg);\n                else\n                    return UNKNOWN_VALUE;\n            }\n            try {\n                return op.apply(type.cast(arg));\n            } catch (Exception e) {\n                return new Exceptional(e);\n            }\n        }\n\n        private <A, B> Object processBinaryOp(Expression expr, Class<A> leftType, Class<B> rightType, BiFunction<A, B, ?> op) {\n            if (expr.getArguments().size() != 2)\n                return UNKNOWN_VALUE;\n            Object left = get(expr.getArguments().get(0));\n            if (left == UNKNOWN_VALUE || !leftType.isInstance(left))\n                return UNKNOWN_VALUE;\n            Object right = get(expr.getArguments().get(1));\n            if (right == UNKNOWN_VALUE || !rightType.isInstance(right))\n                return UNKNOWN_VALUE;\n            try {\n                return op.apply(leftType.cast(left), rightType.cast(right));\n            } catch (Exception e) {\n                return new Exceptional(e);\n            }\n        }\n\n        @Override\n        public ContextValues makeEntryState() {\n            return initState;\n        }\n\n        @Override\n        public ContextValues transferState(ContextValues src, Expression expr) {\n            return src.transfer(expr);\n        }\n\n        @Override\n        public ContextValues transferExceptionalState(ContextValues src, Expression expr) {\n            return src.transfer(expr);\n        }\n\n        @Override\n        public TrueFalse<ContextValues> transferConditionalState(ContextValues src, Expression expr) {\n            boolean invert = false;\n            Expression arg = null;\n            Object cst = null;\n            if (expr.getCode() == AstCode.CmpEq || expr.getCode() == AstCode.CmpNe\n                || (expr.getCode() == AstCode.InvokeVirtual && Methods.isEqualsMethod((MethodReference) expr.getOperand()))) {\n                Expression left = expr.getArguments().get(0);\n                Expression right = expr.getArguments().get(1);\n                if(expr.getCode() == AstCode.CmpNe)\n                    invert = !invert;\n                cst = resolveConditional(src, right);\n                if(cst != null && cst != UNKNOWN_VALUE) {\n                    arg = left;  \n                } else {\n                    cst = resolveConditional(src, left);\n                    if(cst != null && cst != UNKNOWN_VALUE) {\n                        arg = right;\n                    }\n                }\n            }\n            Variable var = null;\n            if (arg != null && arg.getCode() == AstCode.Load) {\n                var = (Variable) arg.getOperand();\n            }\n            return var == null ? new TrueFalse<>(src.transfer(expr))\n                    : new TrueFalse<>(src.add(var, cst), src.remove(var), invert);\n        }\n\n        @Override\n        public ContextValues mergeStates(ContextValues s1, ContextValues s2) {\n            return s1.merge(s2);\n        }\n\n        @Override\n        public boolean sameState(ContextValues s1, ContextValues s2) {\n            return s1.equals(s2);\n        }\n\n        @Override\n        public Object makeFact(ContextValues ctx, Expression expr) {\n            switch(expr.getCode()) {\n            case LogicalAnd:\n                return processBinaryOp(expr, Boolean.class, Boolean.class, Boolean::logicalAnd);\n            case LogicalOr:\n                return processBinaryOp(expr, Boolean.class, Boolean.class, Boolean::logicalOr);\n            case TernaryOp: {\n                Object cond = get(expr.getArguments().get(0));\n                Object left = get(expr.getArguments().get(1));\n                Object right = get(expr.getArguments().get(2));\n                if(Integer.valueOf(1).equals(cond) || Boolean.TRUE.equals(cond)) {\n                    return left; \n                }\n                if(Integer.valueOf(0).equals(cond) || Boolean.FALSE.equals(cond)) {\n                    return right;\n                }\n                return mergeFacts(left, right);\n            }\n            case Store:\n                return resolve(ctx, expr.getArguments().get(0));\n            case LdC:\n                return expr.getOperand();\n            case ArrayLength: {\n                Integer len = getArrayLength(expr.getArguments().get(0));\n                return len == null ? UNKNOWN_VALUE : len;\n            }\n            case CmpEq:\n                return processCmpEq(expr);\n            case CmpNe:\n                return processCmpNe(expr);\n            case CmpLt:\n                return processCmpLt(expr);\n            case CmpLe:\n                return processCmpLe(expr);\n            case CmpGt:\n                return processCmpGt(expr);\n            case CmpGe:\n                return processCmpGe(expr);\n            case Add:\n                return processAdd(expr);\n            case Sub:\n                return processSub(expr);\n            case Mul:\n                return processMul(expr);\n            case Div:\n                return processDiv(expr);\n            case Rem:\n                return processRem(expr);\n            case Xor: {\n                switch (getType(expr)) {\n                case Integer:\n                    return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a ^ b);\n                case Long:\n                    return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a ^ b);\n                default:\n                }\n                return UNKNOWN_VALUE;\n            }\n            case Or: {\n                switch (getType(expr)) {\n                case Integer:\n                    return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a | b);\n                case Long:\n                    return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a | b);\n                default:\n                }\n                return UNKNOWN_VALUE;\n            }\n            case And: {\n                switch (getType(expr)) {\n                case Integer:\n                    return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a & b);\n                case Long:\n                    return processBinaryOp(expr, Long.class, Long.class, (a, b) -> a & b);\n                default:\n                }\n                return UNKNOWN_VALUE;\n            }\n            case Shl: {\n                switch (getType(expr)) {\n                case Integer:\n                    return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a << b);\n                case Long:\n                    return processBinaryOp(expr, Long.class, Integer.class, (a, b) -> a << b);\n                default:\n                }\n                return UNKNOWN_VALUE;\n            }\n            case Shr: {\n                switch (getType(expr)) {\n                case Integer:\n                    return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a >> b);\n                case Long:\n                    return processBinaryOp(expr, Long.class, Integer.class, (a, b) -> a >> b);\n                default:\n                }\n                return UNKNOWN_VALUE;\n            }\n            case UShr: {\n                switch (getType(expr)) {\n                case Integer:\n                    return processBinaryOp(expr, Integer.class, Integer.class, (a, b) -> a >>> b);\n                case Long:\n                    return processBinaryOp(expr, Long.class, Integer.class, (a, b) -> a >>> b);\n                default:\n                }\n                return UNKNOWN_VALUE;\n            }\n            case I2L:\n                return processUnaryOp(expr, Integer.class, i -> (long) i);\n            case I2B:\n                return processUnaryOp(expr, Integer.class, i -> (int) (byte) (int) i);\n            case I2C:\n                return processUnaryOp(expr, Integer.class, i -> (int) (char) (int) i);\n            case I2S:\n                return processUnaryOp(expr, Integer.class, i -> (int) (short) (int) i);\n            case I2D:\n                return processUnaryOp(expr, Integer.class, i -> (double) i);\n            case I2F:\n                return processUnaryOp(expr, Integer.class, i -> (float) i);\n            case L2I:\n                return processUnaryOp(expr, Long.class, l -> (int) (long) l);\n            case L2D:\n                return processUnaryOp(expr, Long.class, l -> (double) l);\n            case L2F:\n                return processUnaryOp(expr, Long.class, l -> (float) l);\n            case F2L:\n                return processUnaryOp(expr, Float.class, l -> (long) (float) l);\n            case F2I:\n                return processUnaryOp(expr, Float.class, l -> (int) (float) l);\n            case F2D:\n                return processUnaryOp(expr, Float.class, l -> (double) l);\n            case D2F:\n                return processUnaryOp(expr, Double.class, l -> (float) (double) l);\n            case D2I:\n                return processUnaryOp(expr, Double.class, l -> (int) (double) l);\n            case D2L:\n                return processUnaryOp(expr, Double.class, l -> (long) (double) l);\n            case Neg:\n                return processNeg(expr);\n            case Load:\n            case GetField:\n                return fromSource(ctx, expr);\n            case Inc: {\n                Expression src = ValuesFlow.getSource(expr);\n                if(src.getCode() == SourceAnnotator.UPDATE_TYPE) {\n                    src = src.getArguments().get(0);\n                    Object val = get(src);\n                    if (val instanceof Integer)\n                        return processUnaryOp(expr, Integer.class, inc -> ((int) val) + inc);\n                    else if (val instanceof Long)\n                        return processUnaryOp(expr, Long.class, inc -> ((long) val) + inc);\n                }\n                return UNKNOWN_VALUE;\n            }\n            case InitObject:\n            case InvokeInterface:\n            case InvokeSpecial:\n            case InvokeStatic:\n            case InvokeVirtual: {\n                MethodReference mr = (MethodReference) expr.getOperand();\n                return processKnownMethods(expr, mr);\n            }\n            case GetStatic: {\n                FieldReference fr = ((FieldReference) expr.getOperand());\n                FieldDefinition fd = fr.resolve();\n                if (fd != null && fd.isEnumConstant()) {\n                    return new EnumConstant(fd.getDeclaringType().getInternalName(), fd.getName());\n                }\n                return fromSource(ctx, expr);\n            }\n            default:\n                return UNKNOWN_VALUE;\n            }\n        }\n\n        @Override\n        public Object mergeFacts(Object f1, Object f2) {\n            if(f1 == null)\n                return f2;\n            if(f2 == null)\n                return f1;\n            if(f1 == UNKNOWN_VALUE || f2 == UNKNOWN_VALUE || !Objects.equals(f1, f2))\n                return UNKNOWN_VALUE;\n            return f1;\n        }\n\n        @Override\n        public boolean sameFact(Object f1, Object f2) {\n            return Objects.equals(f1, f2);\n        }\n\n        @Override\n        public Object makeUnknownFact() {\n            return UNKNOWN_VALUE;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/Dataflow.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport com.strobel.decompiler.ast.Expression;\n\n/**\n * @author lan\n *\n */\ninterface Dataflow<FACT, STATE> {\n    public STATE makeEntryState();\n\n    public STATE transferState(STATE src, Expression expr);\n\n    public STATE transferExceptionalState(STATE src, Expression expr);\n    \n    public TrueFalse<STATE> transferConditionalState(STATE src, Expression expr);\n\n    public STATE mergeStates(STATE s1, STATE s2);\n    \n    public boolean sameState(STATE s1, STATE s2);\n    \n    public FACT makeFact(STATE state, Expression expr);\n    \n    public FACT makeUnknownFact();\n    \n    public FACT mergeFacts(FACT f1, FACT f2);\n    \n    public boolean sameFact(FACT f1, FACT f2);\n    \n    public default void onSuccess(STATE exitState) {};\n    \n    public default void onFail(STATE exitState) {};\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/ETypeAnnotator.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MetadataHelper;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.flow.etype.EType;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author shustkost\n *\n */\npublic class ETypeAnnotator extends Annotator<EType> {\n\n    ETypeAnnotator() {\n        super(\"etype\", null);\n    }\n\n    boolean build(CFG cfg) {\n        return cfg.<ContextTypes, EType> runDFA(this, (md, closure) -> new ETypeDataflow(closure == null\n                ? ContextTypes.DEFAULT : closure), 7);\n    }\n\n    public EType resolve(Expression expr) {\n        EType eType = super.get(expr);\n        return eType == null ? EType.UNKNOWN : eType;\n    }\n\n    static class ContextTypes {\n        static final ContextTypes DEFAULT = new ContextTypes(null);\n\n        final Map<Variable, EType> values;\n\n        private ContextTypes(Map<Variable, EType> values) {\n            this.values = values;\n        }\n\n        ContextTypes merge(ContextTypes other) {\n            if (this == other)\n                return this;\n            if (this == DEFAULT || other == DEFAULT)\n                return DEFAULT;\n            Map<Variable, EType> newTypes = new HashMap<>(values);\n            newTypes.keySet().retainAll(other.values.keySet());\n            if (newTypes.isEmpty())\n                return DEFAULT;\n            other.values.forEach((k, v) -> newTypes.compute(k, (oldK, oldV) -> oldV == null ? null\n                    : EType.or(v, oldV).unknownToNull()));\n            return newTypes.isEmpty() ? DEFAULT : new ContextTypes(newTypes);\n        }\n\n        ContextTypes and(Variable var, EType value) {\n            if (values == null) {\n                return new ContextTypes(Collections.singletonMap(var, value));\n            }\n            EType oldType = values.get(var);\n            if (Objects.equals(value, oldType))\n                return this;\n            EType newType = EType.and(oldType, value);\n            if (Objects.equals(newType, oldType))\n                return this;\n            Map<Variable, EType> newTypes = new HashMap<>(values);\n            newTypes.put(var, newType);\n            return new ContextTypes(newTypes);\n        }\n\n        ContextTypes add(Variable var, EType value) {\n            if (value == null || value == EType.UNKNOWN) {\n                return remove(var);\n            }\n            if (values == null) {\n                return new ContextTypes(Collections.singletonMap(var, value));\n            }\n            EType oldType = values.get(var);\n            if (Objects.equals(value, oldType))\n                return this;\n            Map<Variable, EType> newTypes = new HashMap<>(values);\n            newTypes.put(var, value);\n            return new ContextTypes(newTypes);\n        }\n        \n        ContextTypes remove(Variable var) {\n            if (values != null && values.containsKey(var)) {\n                if (values.size() == 1)\n                    return DEFAULT;\n                Map<Variable, EType> newTypes = new HashMap<>(values);\n                newTypes.remove(var);\n                return new ContextTypes(newTypes);\n            }\n            return this;\n        }\n\n        ContextTypes transfer(Expression expr) {\n            if(expr.getCode() == AstCode.Store) {\n                return add((Variable) expr.getOperand(), Inf.ETYPE.get(expr.getArguments().get(0)));\n            }\n            return this;\n        }\n\n        EType resolve(Expression expr) {\n            Object oper = expr.getOperand();\n            EType result = oper instanceof Variable && values != null ? values.get(oper) : null;\n            return result == EType.UNKNOWN ? null : result;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (obj == null || getClass() != obj.getClass())\n                return false;\n            ContextTypes other = (ContextTypes) obj;\n            return Objects.equals(values, other.values);\n        }\n\n        @Override\n        public String toString() {\n            return values == null ? \"{}\" : values.toString();\n        }\n\n    }\n\n    class ETypeDataflow implements Dataflow<EType, ContextTypes> {\n        private final ContextTypes initial;\n\n        ETypeDataflow(ContextTypes initial) {\n            this.initial = initial;\n        }\n\n        @Override\n        public ContextTypes makeEntryState() {\n            return initial;\n        }\n\n        @Override\n        public ContextTypes transferState(ContextTypes src, Expression expr) {\n            if (expr.getCode() == AstCode.CheckCast) {\n                Expression arg = expr.getArguments().get(0);\n                if (arg.getCode() == AstCode.Load) {\n                    Variable var = (Variable) arg.getOperand();\n                    EType type = EType.subType((TypeReference) expr.getOperand());\n                    return src.and(var, type);\n                }\n            }\n            return src.transfer(expr);\n        }\n\n        @Override\n        public ContextTypes transferExceptionalState(ContextTypes src, Expression expr) {\n            if (expr.getCode() == AstCode.CheckCast) {\n                Expression arg = expr.getArguments().get(0);\n                if (arg.getCode() == AstCode.Load) {\n                    Variable var = (Variable) arg.getOperand();\n                    EType type = EType.subType((TypeReference) expr.getOperand()).negate();\n                    return src.and(var, type);\n                }\n            }\n            return src;\n        }\n\n        @Override\n        public TrueFalse<ContextTypes> transferConditionalState(ContextTypes src, Expression expr) {\n            boolean invert = false;\n            Variable var = null;\n            EType etype = null;\n            if (expr.getCode() == AstCode.InstanceOf) {\n                Expression arg = expr.getArguments().get(0);\n                if (arg.getCode() == AstCode.Load) {\n                    var = (Variable) arg.getOperand();\n                    etype = EType.subType((TypeReference) expr.getOperand());\n                }\n            } else if (expr.getCode() == AstCode.CmpEq || expr.getCode() == AstCode.CmpNe || (expr\n                    .getCode() == AstCode.InvokeVirtual && Methods.isEqualsMethod((MethodReference) expr\n                            .getOperand()))) {\n                if(expr.getCode() == AstCode.CmpNe)\n                    invert = !invert;\n                Expression left = expr.getArguments().get(0);\n                Expression right = expr.getArguments().get(1);\n                Object clazz = Inf.CONST.getValue(right);\n                Expression arg = null;\n                if(clazz instanceof TypeReference) {\n                    arg = left;\n                } else {\n                    clazz = Inf.CONST.getValue(left);\n                    if(clazz instanceof TypeReference) {\n                        arg = right;\n                    }\n                }\n                if(arg != null && arg.getCode() == AstCode.InvokeVirtual && Methods.isGetClass((MethodReference) arg.getOperand())) {\n                    Expression target = arg.getArguments().get(0);\n                    if(target.getCode() == AstCode.Load) {\n                        var = (Variable) target.getOperand();\n                        etype = EType.exact((TypeReference) clazz);\n                    }\n                }\n            } else if (expr.getCode() == AstCode.InvokeVirtual) {\n                MethodReference mr = (MethodReference) expr.getOperand();\n                if(mr.getName().equals(\"isInstance\") && Types.is(mr.getDeclaringType(), Class.class)) {\n                    Object clazz = Inf.CONST.getValue(expr.getArguments().get(0));\n                    Expression target = expr.getArguments().get(1);\n                    if(clazz instanceof TypeReference && target.getCode() == AstCode.Load) {\n                        var = (Variable) target.getOperand();\n                        etype = EType.subType((TypeReference) clazz);\n                    }\n                }\n            }\n            if (var != null) {\n                return new TrueFalse<>(src.and(var, etype), src.and(var, etype.negate()), invert);\n            }\n            return new TrueFalse<>(src);\n        }\n\n        @Override\n        public ContextTypes mergeStates(ContextTypes s1, ContextTypes s2) {\n            return s1.merge(s2);\n        }\n\n        @Override\n        public boolean sameState(ContextTypes s1, ContextTypes s2) {\n            return s1.equals(s2);\n        }\n\n        @Override\n        public EType makeFact(ContextTypes state, Expression expr) {\n            switch (expr.getCode()) {\n            case TernaryOp: {\n                Object cond = Inf.CONST.get(expr.getArguments().get(0));\n                EType left = get(expr.getArguments().get(1));\n                EType right = get(expr.getArguments().get(2));\n                if (Integer.valueOf(1).equals(cond) || Boolean.TRUE.equals(cond)) {\n                    return left;\n                }\n                if (Integer.valueOf(0).equals(cond) || Boolean.FALSE.equals(cond)) {\n                    return right;\n                }\n                return EType.or(left, right);\n            }\n            case Load: {\n                Variable v = (Variable) expr.getOperand();\n                TypeReference varType = v.getType();\n                if(v.getOriginalParameter() != null)\n                    varType = v.getOriginalParameter().getParameterType();\n                EType etype = EType.and(state.resolve(expr), EType.and(fromSource(state, expr), EType.subType(\n                    MetadataHelper.erase(varType))));\n                return etype == null ? EType.UNKNOWN : etype;\n            }\n            case GetField:\n            case GetStatic: {\n                EType etype = EType.and(state.resolve(expr), EType.and(fromSource(state, expr), EType.subType(\n                    ((FieldReference) expr.getOperand()).getFieldType())));\n                return etype == null ? EType.UNKNOWN : etype;\n            }\n            case NewArray:\n                return EType.exact(((TypeReference)expr.getOperand()).makeArrayType());\n            case InitObject:\n            case InitArray:\n            case MultiANewArray:\n                return EType.exact(expr.getInferredType());\n            case InvokeVirtual:\n            case InvokeStatic:\n            case InvokeSpecial:\n            case InvokeInterface: {\n                MethodReference mr = (MethodReference) expr.getOperand();\n                return EType.subType(MetadataHelper.erase(mr).getReturnType());\n            }\n            case CheckCast:\n                return EType.and(EType.subType(MetadataHelper.erase((TypeReference) expr.getOperand())), get(expr\n                        .getArguments().get(0)));\n            case Store:\n            case PutStatic:\n                return get(expr.getArguments().get(0));\n            case PutField:\n                return get(expr.getArguments().get(1));\n            case StoreElement:\n                return get(expr.getArguments().get(2));\n            case LoadElement:\n                return EType.subType(expr.getInferredType());\n            default:\n                return EType.UNKNOWN;\n            }\n        }\n\n        @Override\n        public EType makeUnknownFact() {\n            return EType.UNKNOWN;\n        }\n\n        @Override\n        public EType mergeFacts(EType f1, EType f2) {\n            return EType.or(f1, f2);\n        }\n\n        @Override\n        public boolean sameFact(EType f1, EType f2) {\n            return Objects.equals(f1, f2);\n        }\n\n        private EType resolve(ContextTypes ctx, Expression expr) {\n            if (expr.getCode() == AstCode.LdC) {\n                return EType.exact(expr.getInferredType());\n            }\n            return EType.and(get(expr), ctx.resolve(expr));\n        }\n\n        private EType fromSource(ContextTypes ctx, Expression expr) {\n            Expression src = ValuesFlow.getSource(expr);\n            if (src == expr)\n                return EType.UNKNOWN;\n            EType value = resolve(ctx, src);\n            if (value != null)\n                return value;\n            if (src.getCode() == SourceAnnotator.PHI_TYPE) {\n                for (Expression child : src.getArguments()) {\n                    EType newVal = resolve(ctx, child);\n                    if (newVal == null) {\n                        if(child.getOperand() instanceof ParameterDefinition) {\n                            ParameterDefinition pd = (ParameterDefinition) child.getOperand();\n                            newVal = EType.subType(MetadataHelper.erase(pd.getParameterType()));\n                        } else\n                            return EType.UNKNOWN;\n                    }\n                    if (value == null) {\n                        value = newVal;\n                    } else {\n                        value = EType.or(value, newVal);\n                    }\n                    if (value == EType.UNKNOWN)\n                        return EType.UNKNOWN;\n                }\n                return value;\n            }\n            return EType.UNKNOWN;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/EnumConstant.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.Objects;\n\nimport one.util.huntbugs.warning.WarningAnnotation;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic final class EnumConstant {\n    private final String typeName;\n    private final String name;\n\n    EnumConstant(String typeName, String name) {\n        super();\n        this.typeName = Objects.requireNonNull(typeName);\n        this.name = Objects.requireNonNull(name);\n    }\n\n    public String getTypeName() {\n        return typeName;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public int hashCode() {\n        return typeName.hashCode() * 31 + name.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null || getClass() != obj.getClass())\n            return false;\n        EnumConstant other = (EnumConstant) obj;\n        return name.equals(other.name) && typeName.equals(other.typeName);\n    }\n\n    @Override\n    public String toString() {\n        return new WarningAnnotation.TypeInfo(typeName).getSimpleName()+\".\"+name;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/Exceptional.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport one.util.huntbugs.warning.WarningAnnotation.TypeInfo;\n\n/**\n * @author lan\n *\n */\npublic class Exceptional {\n    private final TypeInfo type;\n\n    Exceptional(Throwable t) {\n        this.type = new TypeInfo(t.getClass().getName().replace('.', '/'));\n    }\n    \n    public TypeInfo getType() {\n        return type;\n    }\n\n    @Override\n    public int hashCode() {\n        return type.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null || getClass() != obj.getClass())\n            return false;\n        Exceptional other = (Exceptional) obj;\n        return type.equals(other.type);\n    }\n    \n    @Override\n    public String toString() {\n        return \"Exception[\"+type+\"]\";\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/FrameContext.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n\n\n\n\n\n\n\nimport one.util.huntbugs.flow.SourceAnnotator.Frame;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Maps;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n\n\n\n\n\n\n\nimport com.strobel.assembler.metadata.JvmType;\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\n/**\n * @author lan\n *\n */\nclass FrameContext {\n    final MethodDefinition md;\n    final ClassFields cf;\n    private final Map<Expression, Expression> updatedNodes = new HashMap<>();\n    \n    FrameContext(MethodDefinition md, ClassFields cf) {\n        this.md = md;\n        this.cf = cf;\n    }\n    \n    boolean isThis(Expression expr) {\n        return !md.isStatic() && Exprs.isThis(expr);\n    }\n    \n    Map<MemberInfo, Expression> getCtorFields(MemberReference ctor) {\n        return cf.ctorFields.get(new MemberInfo(ctor));\n    }\n    \n    Map<MemberInfo, Expression> getInitialFields() {\n        Map<MemberInfo, Expression> map = new HashMap<>();\n        if(md.isConstructor()) {\n            cf.fields.forEach((mi, fd) -> {\n                if(!fd.isStatic())\n                    map.put(mi, getInitialExpression(fd.getFieldType().getSimpleType()));\n                else {\n                    Expression expr = cf.values.get(mi);\n                    if(expr != null)\n                        map.put(mi, expr);\n                }\n            });\n        } else if(md.isTypeInitializer()) {\n            cf.fields.forEach((mi, fd) -> {\n                if(fd.isStatic())\n                    map.put(mi, fd.getConstantValue() != null ? constant(fd.getConstantValue())\n                            : getInitialExpression(fd.getFieldType().getSimpleType()));\n            });\n        } else if((md.getName().equals(\"readResolve\") || md.getName().equals(\"readObjectNoData\"))\n                && md.getSignature().startsWith(\"()\") ||\n                md.getName().equals(\"readObject\") && md.getSignature().equals(\"(Ljava/io/ObjectInputStream;)V\")){\n            cf.fields.forEach((mi, fd) -> {\n                if(fd.isStatic()) {\n                    Expression expr = cf.values.get(mi);\n                    if(expr != null)\n                        map.put(mi, expr);\n                }\n            });\n        } else {\n            map.putAll(cf.values);\n        }\n        return Maps.compactify(map);\n    }\n    \n    private static Expression constant(Object val) {\n        Expression expr = new Expression(AstCode.LdC, val, 0);\n        Inf.CONST.put(expr, val);\n        return expr;\n    }\n\n    private static Expression getInitialExpression(JvmType simpleType) {\n        switch(simpleType)\n        {\n        case Array:\n        case Object:\n            return new Expression(AstCode.AConstNull, null, 0);\n        case Integer:\n        case Byte:\n        case Short:\n        case Boolean:\n        case Character:\n            return constant(0);\n        case Double:\n            return constant(0.0);\n        case Float:\n            return constant(0.0f);\n        case Long:\n            return constant(0L);\n        default:\n            throw new InternalError(\"Unexpected simple type: \"+simpleType);\n        }\n    }\n\n    Expression makeUpdatedNode(Expression src) {\n        if(src.getCode() == SourceAnnotator.UPDATE_TYPE)\n            return src;\n        return updatedNodes.computeIfAbsent(src, s -> new Expression(SourceAnnotator.UPDATE_TYPE, null, s.getOffset(), s));\n    }\n\n    public void makeFieldsFrom(Frame frame) {\n        if(frame == null)\n            return;\n        if(md.isTypeInitializer()) {\n            cf.setStaticFinalFields(frame);\n        } else if(md.isConstructor()) {\n            cf.mergeConstructor(md, frame, this);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/GraphSearch.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.CFG.EdgeType;\n\n/**\n * @author lan\n *\n */\npublic interface GraphSearch<T> {\n    T markStart(Expression expr, boolean isEntry);\n    T transfer(T orig, Expression from, EdgeType edge, Expression to);\n    T merge(T f1, T f2);\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/Inf.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\n/**\n * Publicly available annotators\n * \n * @author Tagir Valeev\n */\npublic class Inf {\n    /**\n     * Annotator which can determine the sources of given expression \n     */\n    public static final SourceAnnotator SOURCE = new SourceAnnotator();\n    \n    /**\n     * Annotator which can determine the statically known constant value of given expression (if any)\n     */\n    public static final ConstAnnotator CONST = new ConstAnnotator();\n    \n    /**\n     * Annotator which can determine whether given expression is subtype of given type or not\n     */\n    public static final ETypeAnnotator ETYPE = new ETypeAnnotator();\n    \n    /**\n     * Annotator which can determine whether given expression can be null\n     */\n    public static final NullAnnotator NULL = new NullAnnotator();\n    \n    /**\n     * Annotator which can find the usages of given expression\n     */\n    public static final BackLinkAnnotator BACKLINK = new BackLinkAnnotator();\n    \n    /**\n     * Annotator which can determine the purity of given expression\n     */\n    public static final PurityAnnotator PURITY = new PurityAnnotator();\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/NullAnnotator.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.flow.Nullness.NullState;\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Methods;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class NullAnnotator extends Annotator<Nullness> {\n\n    NullAnnotator() {\n        super(\"null\", null);\n    }\n\n    boolean build(CFG cfg) {\n        return cfg.<ContextNulls, Nullness> runDFA(this, (md, closure) -> new NullDataflow(md,\n                closure == null ? ContextNulls.DEFAULT : closure), 7);\n    }\n\n    public Nullness resolve(Expression expr) {\n        Nullness nullability = super.get(expr);\n        return nullability == null ? Nullness.UNKNOWN : nullability;\n    }\n\n    static class ContextNulls {\n        static final ContextNulls DEFAULT = new ContextNulls(null);\n\n        final Map<Variable, Nullness> values;\n\n        private ContextNulls(Map<Variable, Nullness> values) {\n            this.values = values;\n        }\n\n        ContextNulls merge(ContextNulls other) {\n            if (this == other)\n                return this;\n            if (values == null || other.values == null)\n                return DEFAULT;\n            Set<Variable> vars = new HashSet<>(values.keySet());\n            vars.addAll(other.values.keySet());\n            Map<Variable, Nullness> newNulls = new HashMap<>();\n            for(Variable v : vars) {\n                Nullness n1 = get(values, v);\n                Nullness n2 = get(other.values, v);\n                if(n1 != null && n2 != null) {\n                    Nullness n = n1.or(n2);\n                    if(n != null)\n                        newNulls.put(v, n);\n                }\n            }\n            return newNulls.isEmpty() ? DEFAULT : new ContextNulls(newNulls);\n        }\n\n        private static Nullness get(Map<Variable, Nullness> map, Variable v) {\n            Nullness nullness = map.get(v);\n            if(nullness != null)\n                return nullness;\n            ParameterDefinition pd = v.getOriginalParameter();\n            if(pd != null) {\n                return Nullness.UNKNOWN_AT_ENTRY;\n            }\n            return null;\n        }\n\n        ContextNulls add(Variable var, Nullness value) {\n            if (values == null) {\n                return new ContextNulls(Collections.singletonMap(var, value));\n            }\n            Nullness oldNullability = values.get(var);\n            if (Objects.equals(value, oldNullability))\n                return this;\n            Map<Variable, Nullness> newNulls = new HashMap<>(values);\n            newNulls.put(var, value);\n            return new ContextNulls(newNulls);\n        }\n\n        ContextNulls remove(Variable var) {\n            if (values != null && values.containsKey(var)) {\n                if (values.size() == 1)\n                    return DEFAULT;\n                Map<Variable, Nullness> newNulls = new HashMap<>(values);\n                newNulls.remove(var);\n                return new ContextNulls(newNulls);\n            }\n            return this;\n        }\n\n        ContextNulls transfer(Expression expr) {\n            if (expr.getCode() == AstCode.Store) {\n                Nullness value = Inf.NULL.get(expr.getArguments().get(0));\n                if(value == Nullness.UNKNOWN)\n                    value = Nullness.createAt(expr, NullState.UNKNOWN);\n                return add((Variable) expr.getOperand(), value);\n            }\n            return this;\n        }\n\n        Nullness resolve(Expression expr) {\n            Object oper = expr.getOperand();\n            Nullness result = oper instanceof Variable && values != null ? values.get(oper) : null;\n            return result == null ? Nullness.UNKNOWN : result;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (obj == null || getClass() != obj.getClass())\n                return false;\n            ContextNulls other = (ContextNulls) obj;\n            return Objects.equals(values, other.values);\n        }\n\n        @Override\n        public String toString() {\n            return values == null ? \"{}\" : values.toString();\n        }\n\n    }\n\n    class NullDataflow implements Dataflow<Nullness, ContextNulls> {\n        private final ContextNulls initial;\n        private final MethodDefinition md;\n\n        NullDataflow(MethodDefinition md, ContextNulls initial) {\n            this.initial = initial;\n            this.md = md;\n        }\n\n        @Override\n        public ContextNulls makeEntryState() {\n            return initial;\n        }\n\n        @Override\n        public ContextNulls transferState(ContextNulls src, Expression expr) {\n            switch (expr.getCode()) {\n            case MonitorEnter:\n            case MonitorExit:\n            case GetField:\n            case PutField:\n            case InvokeInterface:\n            case InvokeSpecial:\n            case InvokeVirtual:\n            case StoreElement:\n            case LoadElement: {\n                Expression arg = expr.getArguments().get(0);\n                if (arg.getCode() == AstCode.Load) {\n                    Variable var = (Variable) arg.getOperand();\n                    return src.add(var, Nullness.createAt(expr, NullState.NONNULL_DEREF));\n                }\n                return src;\n            }\n            case InvokeStatic: {\n                MethodReference mr = (MethodReference) expr.getOperand();\n                String name = mr.getName();\n                String typeName = mr.getDeclaringType().getInternalName();\n                if (typeName.endsWith(\"/Assert\") && name.equals(\"assertNotNull\")\n                    || typeName.equals(\"com/google/common/base/Preconditions\") && name.equals(\"checkNotNull\")\n                    || typeName.equals(\"java/util/Objects\") && name.equals(\"requireNonNull\")) {\n                    if (expr.getArguments().size() == 1) {\n                        Expression arg = expr.getArguments().get(0);\n                        if (arg.getCode() == AstCode.Load) {\n                            Variable var = (Variable) arg.getOperand();\n                            return src.add(var, Nullness.createAt(expr, NullState.NONNULL_CHECKED));\n                        }\n                    }\n                    if (expr.getArguments().size() == 2) {\n                        Expression arg = null;\n                        if (mr.getErasedSignature().startsWith(\"(Ljava/lang/Object;\")) {\n                            arg = expr.getArguments().get(0);\n                        } else if (mr.getErasedSignature().startsWith(\"(Ljava/lang/String;Ljava/lang/Object;)\")) {\n                            arg = expr.getArguments().get(1);\n                        }\n                        if (arg != null && arg.getCode() == AstCode.Load) {\n                            Variable var = (Variable) arg.getOperand();\n                            return src.add(var, Nullness.createAt(expr, NullState.NONNULL_CHECKED));\n                        }\n                    }\n                }\n                break;\n            }\n            default:\n            }\n            return src.transfer(expr);\n        }\n\n        @Override\n        public ContextNulls transferExceptionalState(ContextNulls src, Expression expr) {\n            return src;\n        }\n\n        @Override\n        public TrueFalse<ContextNulls> transferConditionalState(ContextNulls src, Expression expr) {\n            src = transferState(src, expr);\n            boolean invert = false;\n            Variable var;\n            if (expr.getCode() == AstCode.InstanceOf) {\n                Expression arg = expr.getArguments().get(0);\n                if (arg.getCode() == AstCode.Load) {\n                    var = (Variable) arg.getOperand();\n                    return new TrueFalse<>(src.add(var, Nullness.createAt(expr, NullState.NONNULL_CHECKED)), src, invert);\n                }\n            } else if (expr.getCode() == AstCode.CmpEq || expr.getCode() == AstCode.CmpNe) {\n                if (expr.getCode() == AstCode.CmpNe)\n                    invert = !invert;\n                Expression left = expr.getArguments().get(0);\n                Expression right = expr.getArguments().get(1);\n                ContextNulls trueSrc = src;\n                if (left.getCode() == AstCode.Load) {\n                    var = (Variable) left.getOperand();\n                    Nullness nullness = get(right);\n                    if (nullness != null && nullness.isNull())\n                        return new TrueFalse<>(src.add(var, Nullness.nullAt(expr)), src.add(var, Nullness.createAt(\n                            expr, NullState.NONNULL_CHECKED)), invert);\n                    trueSrc = src.add(var, nullness);\n                }\n                if (right.getCode() == AstCode.Load) {\n                    var = (Variable) right.getOperand();\n                    Nullness nullness = get(left);\n                    if (nullness != null && nullness.isNull())\n                        return new TrueFalse<>(trueSrc.add(var, Nullness.nullAt(expr)), src.add(var, Nullness.createAt(\n                            expr, NullState.NONNULL_CHECKED)), invert);\n                    return new TrueFalse<>(trueSrc.add(var, nullness), src, invert);\n                }\n            } else if (expr.getCode() == AstCode.InvokeVirtual\n                && Methods.isEqualsMethod((MethodReference) expr.getOperand())) {\n                Expression arg = expr.getArguments().get(1);\n                if (arg.getCode() == AstCode.Load) {\n                    var = (Variable) arg.getOperand();\n                    return new TrueFalse<>(src.add(var, Nullness.createAt(expr, NullState.NONNULL_CHECKED)), src,\n                            invert);\n                }\n            }\n            return new TrueFalse<>(src);\n        }\n\n        @Override\n        public ContextNulls mergeStates(ContextNulls s1, ContextNulls s2) {\n            return s1.merge(s2);\n        }\n\n        @Override\n        public boolean sameState(ContextNulls s1, ContextNulls s2) {\n            return s1.equals(s2);\n        }\n\n        @Override\n        public Nullness makeFact(ContextNulls state, Expression expr) {\n            if (Inf.CONST.getValue(expr) != null)\n                return Nullness.createAt(expr, NullState.NONNULL);\n            switch (expr.getCode()) {\n            case TernaryOp: {\n                Object cond = Inf.CONST.get(expr.getArguments().get(0));\n                Nullness left = get(expr.getArguments().get(1));\n                Nullness right = get(expr.getArguments().get(2));\n                if (Integer.valueOf(1).equals(cond) || Boolean.TRUE.equals(cond)) {\n                    return left;\n                }\n                if (Integer.valueOf(0).equals(cond) || Boolean.FALSE.equals(cond)) {\n                    return right;\n                }\n                if(left == right)\n                    return left;\n                if(left == Nullness.UNKNOWN)\n                    left = Nullness.createAt(expr.getArguments().get(1), NullState.UNKNOWN); \n                if(right == Nullness.UNKNOWN)\n                    right = Nullness.createAt(expr.getArguments().get(2), NullState.UNKNOWN); \n                return left.or(right);\n            }\n            case Load:\n                if (!md.isStatic() && Exprs.isThis(expr))\n                    return Nullness.createAt(expr, NullState.NONNULL);\n                return state.resolve(expr);\n            case GetField:\n                return Nullness.UNKNOWN;\n            case GetStatic: {\n                FieldReference fr = (FieldReference) expr.getOperand();\n                FieldDefinition fd = fr.resolve();\n                if (fd != null && fd.isEnumConstant())\n                    return Nullness.createAt(expr, NullState.NONNULL);\n                return Nullness.UNKNOWN;\n            }\n            case InitObject:\n            case InitArray:\n            case MultiANewArray:\n            case NewArray:\n                return Nullness.createAt(expr, NullState.NONNULL);\n            case CheckCast:\n            case Store:\n            case PutStatic:\n                return get(expr.getArguments().get(0));\n            case PutField:\n                return get(expr.getArguments().get(1));\n            case StoreElement:\n                return get(expr.getArguments().get(2));\n            case AConstNull:\n                return Nullness.nullAt(expr);\n            default:\n                return Nullness.UNKNOWN;\n            }\n        }\n\n        @Override\n        public Nullness makeUnknownFact() {\n            return Nullness.UNKNOWN;\n        }\n\n        @Override\n        public Nullness mergeFacts(Nullness f1, Nullness f2) {\n            if (f1 == null)\n                return f2;\n            return f1.or(f2);\n        }\n\n        @Override\n        public boolean sameFact(Nullness f1, Nullness f2) {\n            return Objects.equals(f1, f2);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/Nullness.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\nimport one.util.huntbugs.flow.CFG.EdgeType;\nimport one.util.huntbugs.flow.CFG.SearchResult;\n\n/**\n * @author Tagir Valeev\n */\npublic class Nullness {\n    public static enum NullState {\n        UNKNOWN, NONNULL, NONNULL_DEREF, NONNULL_CHECKED, NULL, NULLABLE, NULL_EXCEPTIONAL,\n        // Flow from assignment to current expression goes through explicit throw\n        // (internal state)\n        INT_EXPLICIT_THROW,\n        // Flow from assignment to current expression goes through suspicious call\n        // which will probably never return normally (like Assert.fail())\n        SUSPICIOUS_CALL;\n        \n        NullState or(NullState other) {\n            if(this == other)\n                return this;\n            if(this == NULL || other == NULL || this == NULLABLE || other == NULLABLE)\n                return NULLABLE;\n            if(this != UNKNOWN && other != UNKNOWN)\n                return NONNULL;\n            return UNKNOWN;\n        }\n        \n        public boolean isNull() {\n            return this == NULL;\n        }\n\n        public boolean isNonNull() {\n            switch(this) {\n            case NONNULL:\n            case NONNULL_CHECKED:\n            case NONNULL_DEREF:\n                return true;\n            default:\n                return false;\n            }\n        }\n    }\n    \n    static final Expression ENTRY_EXPRESSION = new Expression(AstCode.Nop, null, -1); \n\n    public static final Nullness UNKNOWN = new Nullness(Collections.emptyMap());\n\n    static final Nullness UNKNOWN_AT_ENTRY = createAt(ENTRY_EXPRESSION, NullState.UNKNOWN);\n\n    private final Map<Expression, NullState> expressions;\n\n    private Nullness(Map<Expression, NullState> expressions) {\n        this.expressions = expressions;\n    }\n    \n    private NullState state() {\n        NullState n = null;\n        for(NullState nn : expressions.values()) {\n            if(n == null)\n                n = nn;\n            else if(n != nn)\n                return null;\n        }\n        return n;\n    }\n\n    public NullState stateAt(CFG cfg, Expression target) {\n        if(this == UNKNOWN)\n            return NullState.UNKNOWN;\n        NullState state = state();\n        if(state != null && state != NullState.NULLABLE)\n            return state;\n        boolean hasNullable = false, hasUnknown = false, hasNonnull = false;\n        for(NullState nn : expressions.values()) {\n            switch(nn) {\n            case NONNULL:\n            case NONNULL_CHECKED:\n            case NONNULL_DEREF:\n                hasNonnull = true;\n                break;\n            case NULL:\n            case NULLABLE:\n                hasNullable = true;\n                break;\n            default:\n                hasUnknown = true;\n                break;\n            }\n        }\n        if(hasNonnull && !hasNullable && !hasUnknown)\n            return NullState.NONNULL;\n//        if(hasNullable && !hasNonnull && !hasUnknown)\n//            return NullState.NULLABLE;\n        if(cfg == null || (hasUnknown && !hasNullable))\n            return NullState.UNKNOWN;\n        SearchResult<NullState> sr = cfg.graphSearch(new NullGraphSearch(target));\n        if(sr.atExit() != null || sr.atFail() == NullState.INT_EXPLICIT_THROW)\n            return NullState.UNKNOWN;\n        NullState res = sr.atExpression(target);\n        return res;\n    }\n    \n    class NullGraphSearch implements GraphSearch<NullState> {\n        private final Expression target;\n\n        public NullGraphSearch(Expression target) {\n            this.target = target;\n        }\n\n        @Override\n        public NullState markStart(Expression expr, boolean isEntry) {\n            NullState state = expressions.get(expr);\n            return (state == null && isEntry) ? expressions.get(ENTRY_EXPRESSION) : state;\n        }\n\n        @Override\n        public NullState transfer(NullState orig, Expression from, EdgeType edge, Expression to) {\n            NullState state = expressions.get(to);\n            if(state != null)\n                return state;\n            if(from == target)\n                return null;\n            if(orig == NullState.NULL || orig == NullState.NULL_EXCEPTIONAL || orig == NullState.NULLABLE) {\n                switch (from.getCode()) {\n                case InvokeInterface:\n                case InvokeSpecial:\n                case InvokeStatic:\n                case InvokeVirtual:\n                    MethodReference mr = (MethodReference) from.getOperand();\n                    String lcName = mr.getName().toLowerCase(Locale.ENGLISH);\n                    if (lcName.contains(\"error\") && !mr.getDeclaringType().getSimpleName().contains(\"Log\")\n                        || lcName.startsWith(\"throw\") || lcName.startsWith(\"fail\"))\n                        return NullState.SUSPICIOUS_CALL;\n                default:\n                }\n            }\n            if((orig == NullState.NULL || orig == NullState.NULLABLE) && edge == EdgeType.FAIL) {\n                if(to == null && from.getCode() == AstCode.AThrow)\n                    return NullState.INT_EXPLICIT_THROW;\n                return NullState.NULL_EXCEPTIONAL;\n            }\n            return orig;\n        }\n\n        @Override\n        public NullState merge(NullState f1, NullState f2) {\n            if(f1 == null || f1 == f2)\n                return f2;\n            if(f2 == null)\n                return f1;\n            if(f1 == NullState.SUSPICIOUS_CALL || f2 == NullState.SUSPICIOUS_CALL)\n                return NullState.SUSPICIOUS_CALL;\n            if(f1 == NullState.INT_EXPLICIT_THROW || f2 == NullState.INT_EXPLICIT_THROW)\n                return NullState.INT_EXPLICIT_THROW;\n            if(f1.isNonNull() && f2.isNonNull())\n                return NullState.NONNULL;\n            if(f1 == NullState.NULL || f1 == NullState.NULLABLE || f2 == NullState.NULL || f2 == NullState.NULLABLE)\n                return NullState.NULLABLE;\n            if(f1 == NullState.NULL_EXCEPTIONAL || f2 == NullState.NULL_EXCEPTIONAL)\n                return NullState.NULL_EXCEPTIONAL;\n            return NullState.UNKNOWN;\n        }\n        \n    }\n    \n    public boolean isNull() {\n        return state() == NullState.NULL;\n    }\n\n    Nullness or(Nullness other) {\n        if (this == other)\n            return this;\n        Map<Expression, NullState> newExprs = new HashMap<>(expressions);\n        other.expressions.forEach((expr, ns) -> newExprs.merge(expr, ns, NullState::or));\n        return new Nullness(newExprs);\n    }\n    \n    Nullness unknownToNull() {\n        return this == UNKNOWN ? null : this;\n    }\n\n    public boolean isNonNull() {\n        NullState state = state();\n        return state != null && state.isNonNull();\n    }\n\n    public static Nullness nullAt(Expression expr) {\n        return createAt(expr, NullState.NULL);\n    }\n    \n    public static Nullness createAt(Expression expr, NullState state) {\n        return new Nullness(Collections.singletonMap(expr, state));\n    }\n\n    @Override\n    public int hashCode() {\n        return expressions.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null || getClass() != obj.getClass())\n            return false;\n        Nullness other = (Nullness) obj;\n        return expressions.equals(other.expressions);\n    }\n\n    @Override\n    public String toString() {\n        return expressions.toString();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/PurityAnnotator.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author lan\n *\n */\npublic class PurityAnnotator extends Annotator<PurityAnnotator.Purity> {\n    PurityAnnotator() {\n        super(\"purity\", Purity.HEAP_MOD);\n    }\n\n    public static enum Purity {\n        /**\n         * Does not modify program state and always produces the same (or practically indistinguishable) result.\n         */\n        CONST,\n        /**\n         * Does not modify program state and produces the same (or practically indistinguishable) result\n         * if local variables directly involved into expression don't change.\n         */\n        LOCAL_DEP,\n        /**\n         * Does not modify program state and produces the same (or practically indistinguishable) result\n         * if heap does not change.\n         */\n        HEAP_DEP, \n        /**\n         * Does not modify program state.\n         */\n        SIDE_EFFECT_FREE,\n        /**\n         * May modify heap.\n         */\n        HEAP_MOD, \n        /**\n         * May modify heap and/or local variables.\n         */\n        LOCAL_MOD;\n        \n        Purity merge(Purity other) {\n            return this.ordinal() > other.ordinal() ? this : other;\n        }\n        \n        public boolean atLeast(Purity other) {\n            return this.ordinal() <= other.ordinal();\n        }\n    }\n\n    void annotate(Node node, FrameContext fc) {\n        forExpressions(node, expr -> annotatePurity(expr, fc));\n    }\n    \n    private Purity getOwnPurity(Expression expr, FrameContext fc) {\n        switch (expr.getCode()) {\n            case Inc:\n            case PreIncrement:\n            case PostIncrement:\n            case Store:\n            case CompoundAssignment:\n                return Purity.LOCAL_MOD;\n            case InvokeDynamic:\n            case StoreElement:\n            case PutField:\n            case PutStatic:\n                return Purity.HEAP_MOD;\n            case InitObject: {\n                MethodReference mr = (MethodReference) expr.getOperand();\n                if (!fc.cf.isSideEffectFree(mr, true))\n                    return Purity.HEAP_MOD;\n                if (Types.isImmutable(mr.getDeclaringType()))\n                    return Purity.CONST;\n                return Purity.SIDE_EFFECT_FREE;\n            }\n            case NewArray:\n            case InitArray:\n                return Purity.SIDE_EFFECT_FREE;\n            case GetField:\n                if(fc.cf.isKnownEffectivelyFinal(new MemberInfo((MemberReference) expr.getOperand()))\n                        && !fc.md.isConstructor()) {\n                    return Purity.CONST;\n                }\n                return Purity.HEAP_DEP;\n            case GetStatic:\n                if(fc.cf.isKnownEffectivelyFinal(new MemberInfo((MemberReference) expr.getOperand()))\n                        && !fc.md.isTypeInitializer()) {\n                    return Purity.CONST;\n                }\n                return Purity.HEAP_DEP;\n            case LoadElement:\n                return Purity.HEAP_DEP;\n            case InvokeSpecial:\n            case InvokeStatic:\n            case InvokeVirtual:\n            case InvokeInterface: {\n                MethodReference mr = (MethodReference) expr.getOperand();\n                if (Methods.isPure(mr)) {\n                    boolean indistinguishable = mr.getReturnType().isVoid() || mr.getReturnType().isPrimitive() || Types.isImmutable(mr.getReturnType());\n                    if(indistinguishable) {\n                        return Purity.CONST;\n                    }\n                    return Purity.SIDE_EFFECT_FREE;\n                }\n                if (fc.cf.isSideEffectFree(mr, expr.getCode() == AstCode.InvokeSpecial)) {\n                    return Purity.SIDE_EFFECT_FREE;\n                }\n                return Purity.HEAP_MOD;\n            }\n            case Load:\n                return Purity.LOCAL_DEP;\n            default:\n                return Purity.CONST;\n        }\n    }\n\n    private Purity annotatePurity(Expression expr, FrameContext fc) {\n        Purity purity = Purity.CONST;\n        for(Expression child : expr.getArguments()) {\n            purity = purity.merge(annotatePurity(child, fc));\n        }\n        if(Inf.CONST.getValue(expr) != null) {\n            // statically known constant\n            purity = Purity.CONST;\n        } else {\n            purity = purity.merge(getOwnPurity(expr, fc));\n        }\n        putIfAbsent(expr, purity);\n        return purity;\n    }\n    \n    @Override\n    public Purity get(Expression expr) {\n        return super.get(expr);\n    }\n    \n    public boolean isPure(Expression expr) {\n        return get(expr).atLeast(Purity.LOCAL_DEP);\n    }\n    \n    public boolean isSideEffectFree(Expression expr) {\n        return get(expr).atLeast(Purity.SIDE_EFFECT_FREE);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/SourceAnnotator.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.IdentityHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.Map.Entry;\nimport java.util.function.UnaryOperator;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\nimport one.util.huntbugs.util.Exprs;\nimport one.util.huntbugs.util.Maps;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class SourceAnnotator extends Annotator<Expression> {\n    static final AstCode PHI_TYPE = AstCode.Wrap;\n    static final AstCode UPDATE_TYPE = AstCode.Nop;\n\n    SourceAnnotator() {\n        super(\"source\", null);\n    }\n    \n    Collection<Expression> build(ClassFields cf, CFG cfg) {\n        AtomicReference<Collection<Expression>> origFrame = new AtomicReference<>();\n        boolean valid = cfg.<Frame, Expression>runDFA(this, (md, closure) -> {\n            SourceDataflow df = new SourceDataflow(cf, md, closure);\n            origFrame.compareAndSet(null, df.origFrame.initial.values());\n            return df;\n        }, 7);\n        return valid ? origFrame.get() : null;\n    }\n    \n    public Expression getSource(Expression input) {\n        Expression source = get(input);\n        return source == null ? input : source;\n    }\n\n    static Stream<Expression> children(Set<Expression> visited, Expression parent) {\n        if(parent.getCode() == PHI_TYPE) {\n            return parent.getArguments().stream();\n        } else if(parent.getCode() == AstCode.TernaryOp) {\n            if(!visited.add(parent))\n                return Stream.empty();\n            return IntStream.of(1, 2).mapToObj(i -> Inf.SOURCE.getSource(parent.getArguments().get(i)))\n                    .flatMap(ch -> children(visited, ch));\n        } else\n            return Stream.of(parent);\n    }\n\n    static Expression makePhiNode(Expression left, Expression right, FrameContext fc) {\n        if (left == null)\n            return right;\n        if (right == null || left == right)\n            return left;\n        if (left.getCode() == UPDATE_TYPE) {\n            Expression leftContent = left.getArguments().get(0);\n            if (leftContent == right)\n                return left;\n            if(right.getCode() == UPDATE_TYPE) {\n                Expression rightContent = right.getArguments().get(0);\n                if(leftContent == rightContent)\n                    return left;\n                return fc.makeUpdatedNode(makePhiNode(leftContent, rightContent, fc));\n            }\n        } else if (right.getCode() == UPDATE_TYPE && right.getArguments().get(0) == left) {\n            return right;\n        }\n        List<Expression> children = new ArrayList<>();\n        children(new HashSet<>(), left).forEach(children::add);\n        int baseSize = children.size();\n        children(new HashSet<>(), right).forEach(child -> {\n            if(!children.contains(child))\n                children.add(child);\n        });\n        if (children.size() == baseSize) {\n            return left;\n        }\n        return new Expression(PHI_TYPE, null, 0, children);\n    }\n\n    static boolean isExprEqual(Expression left, Expression right) {\n        if (left == right)\n            return true;\n        if (left == null || right == null)\n            return false;\n        if (left.getCode() == PHI_TYPE && right.getCode() == PHI_TYPE) {\n            List<Expression> leftArgs = left.getArguments();\n            List<Expression> rightArgs = right.getArguments();\n            if (leftArgs.size() != rightArgs.size())\n                return false;\n            for (Expression arg : rightArgs) {\n                if (!leftArgs.contains(arg))\n                    return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    static class Frame {\n        private final Map<Variable, Expression> sources;\n        private final FrameContext fc;\n        final Map<MemberInfo, Expression> fieldValues;\n        final Map<ParameterDefinition, Expression> initial;\n        \n        Frame(FrameContext fc, Frame closure) {\n            this.sources = new IdentityHashMap<>();\n            this.fieldValues = fc.getInitialFields();\n            this.fc = fc;\n            this.initial = new IdentityHashMap<>();\n            for(ParameterDefinition pd : fc.md.getParameters()) {\n                putInitial(pd);\n            }\n            ParameterDefinition thisParam = fc.md.getBody().getThisParameter();\n            if(thisParam != null) {\n                putInitial(thisParam);\n            }\n            if(closure != null) {\n                initial.putAll(closure.initial);\n                sources.putAll(closure.sources);\n            }\n        }\n    \n        private Frame(Frame parent, Map<Variable, Expression> sources, Map<MemberInfo, Expression> fields) {\n            this.fc = parent.fc;\n            this.initial = parent.initial;\n            this.fieldValues = fields;\n            this.sources = sources;\n        }\n    \n        Expression get(Variable var) {\n            Expression expr = sources.get(var);\n            if(expr != null)\n                return expr;\n            return initial.get(var.getOriginalParameter());\n        }\n    \n        private void putInitial(ParameterDefinition thisParam) {\n            Expression pde = new Expression(AstCode.Load, thisParam, 0);\n            pde.setExpectedType(thisParam.getParameterType());\n            pde.setInferredType(thisParam.getParameterType());\n            initial.put(thisParam, pde);\n        }\n    \n        Frame merge(Frame other, FrameContext fc) {\n            Map<Variable, Expression> res = mergeSources(other, fc);\n            Map<MemberInfo, Expression> resFields = mergeFields(other.fieldValues, fc);\n            if(resFields == null && res == null)\n                return this;\n            if(resFields == null)\n                resFields = fieldValues;\n            if(res == null)\n                res = sources;\n            return new Frame(this, res, resFields);\n        }\n    \n        private Map<MemberInfo, Expression> mergeFields(Map<MemberInfo, Expression> other, FrameContext fc) {\n            Map<MemberInfo, Expression> resFields = null;\n            for (Entry<MemberInfo, Expression> e : fieldValues.entrySet()) {\n                Expression left = e.getValue();\n                Expression right = other.get(e.getKey());\n                Expression phi = left == null || right == null ? null : makePhiNode(left, right, fc);\n                if (phi == left)\n                    continue;\n                if (resFields == null)\n                    resFields = new HashMap<>(fieldValues);\n                resFields.put(e.getKey(), phi);\n            }\n            if(resFields == null)\n                return null;\n            resFields.values().removeIf(Objects::isNull);\n            return Maps.compactify(resFields);\n        }\n    \n        private Map<Variable, Expression> mergeSources(Frame other, FrameContext fc) {\n            Map<Variable, Expression> res = null;\n            for (Entry<Variable, Expression> e : sources.entrySet()) {\n                Expression left = e.getValue();\n                Expression right = other.get(e.getKey());\n                Expression phi = makePhiNode(left, right, fc);\n                if (phi == left)\n                    continue;\n                if (res == null)\n                    res = new IdentityHashMap<>(sources);\n                res.put(e.getKey(), phi);\n            }\n            for(Entry<Variable, Expression> e : other.sources.entrySet()) {\n                if(!sources.containsKey(e.getKey())) {\n                    if (res == null)\n                        res = new IdentityHashMap<>(sources);\n                    res.put(e.getKey(), makePhiNode(e.getValue(), initial.get(e.getKey().getOriginalParameter()), fc));\n                }\n            }\n            return res;\n        }\n    \n        static boolean isEqual(Frame left, Frame right) {\n            if (left == right)\n                return true;\n            Map<Variable, Expression> l = left.sources;\n            Map<Variable, Expression> r = right.sources;\n            if(l.size() != r.size())\n                return false;\n            for(Entry<Variable, Expression> e : l.entrySet()) {\n                if(!isExprEqual(e.getValue(), r.get(e.getKey())))\n                    return false;\n            }\n            Map<MemberInfo, Expression> lf = left.fieldValues;\n            Map<MemberInfo, Expression> rf = right.fieldValues;\n            if(lf.size() != rf.size())\n                return false;\n            for(Entry<MemberInfo, Expression> e : lf.entrySet()) {\n                if(!isExprEqual(e.getValue(), rf.get(e.getKey())))\n                    return false;\n            }\n            return true;\n        }\n    \n        private Frame replace(Variable var, Expression replacement) {\n            Expression expression = get(var);\n            if (expression != replacement) {\n                Map<Variable, Expression> res = new IdentityHashMap<>(sources);\n                res.put(var, replacement);\n                return new Frame(this, res, this.fieldValues);\n            }\n            return this;\n        }\n        \n        private Frame replaceField(FieldReference fr, Expression replacement) {\n            MemberInfo mi = new MemberInfo(fr);\n            if(fieldValues.isEmpty()) {\n                return new Frame(this, this.sources, Collections.singletonMap(mi, replacement));\n            }\n            Expression expression = fieldValues.get(mi);\n            if (expression != replacement) {\n                if(expression != null && fieldValues.size() == 1) {\n                    return new Frame(this, this.sources, Collections.singletonMap(mi, replacement));\n                }\n                Map<MemberInfo, Expression> res = new HashMap<>(fieldValues);\n                res.put(mi, replacement);\n                return new Frame(this, this.sources, res);\n            }\n            return this;\n        }\n        \n        private Frame deleteAllFields() {\n            if(fieldValues.isEmpty())\n                return this;\n            Map<MemberInfo, Expression> res = new HashMap<>();\n            fieldValues.forEach((mi, expr) -> {\n                if(!fc.cf.isKnownFinal(mi)) {\n                    if(expr.getCode() == UPDATE_TYPE) {\n                        res.put(mi, expr);\n                    } else {\n                        res.put(mi, fc.makeUpdatedNode(expr));\n                    }\n                }\n            });\n            return new Frame(this, this.sources, Maps.compactify(res));\n        }\n        \n        private Frame deleteFields() {\n            if(fieldValues.isEmpty())\n                return this;\n            Map<MemberInfo, Expression> res = new HashMap<>();\n            fieldValues.forEach((mi, expr) -> {\n                if(expr.getCode() == UPDATE_TYPE || fc.cf.isKnownEffectivelyFinal(mi)) {\n                    res.put(mi, expr);\n                } else {\n                    res.put(mi, fc.makeUpdatedNode(expr));\n                }\n            });\n            return new Frame(this, this.sources, Maps.compactify(res));\n        }\n    \n        Frame replaceAll(UnaryOperator<Expression> op) {\n            Map<Variable, Expression> res = null;\n            for (Entry<Variable, Expression> e : sources.entrySet()) {\n                Expression expr = op.apply(e.getValue());\n                if (expr != e.getValue()) {\n                    if (res == null)\n                        res = new IdentityHashMap<>(sources);\n                    res.put(e.getKey(), expr);\n                }\n            }\n            return res == null ? this : new Frame(this, res, this.fieldValues);\n        }\n    }\n\n    class SourceDataflow implements Dataflow<Expression, Frame> {\n        private final FrameContext fc;\n        final Frame origFrame;\n\n        SourceDataflow(ClassFields cf, MethodDefinition md, Frame closure) {\n            this.fc = new FrameContext(md, cf);\n            this.origFrame = new Frame(fc, closure);\n        }\n        \n        @Override\n        public Frame makeEntryState() {\n            return origFrame;\n        }\n\n        @Override\n        public Frame transferState(Frame target, Expression expr) {\n            switch (expr.getCode()) {\n            case Store: {\n                Variable var = ((Variable) expr.getOperand());\n                Expression arg = expr.getArguments().get(0);\n                Expression source = get(arg);\n                if(source == null)\n                    source = arg;\n                return target.replace(var, source);\n            }\n            case Inc:\n                if (expr.getOperand() instanceof Variable) {\n                    Variable var = ((Variable) expr.getOperand());\n                    target = target.replace(var, expr);\n                }\n                return target;\n            case PostIncrement:\n            case PreIncrement: {\n                Expression arg = expr.getArguments().get(0);\n                if (arg.getOperand() instanceof Variable) {\n                    return target.replace(((Variable) arg.getOperand()), expr);\n                }\n                if (arg.getCode() == AstCode.GetField) {\n                    FieldReference fr = ((FieldReference) arg.getOperand());\n                    if(fc.isThis(Exprs.getChild(arg, 0))) {\n                        MemberInfo mi = new MemberInfo(fr);\n                        Expression prevExpr = target.fieldValues.get(mi);\n                        if(prevExpr != null)\n                            target = target.replaceField(fr, fc.makeUpdatedNode(prevExpr));\n                    }\n                    return target.replaceAll(src -> src.getCode() == AstCode.GetField\n                        && fr.isEquivalentTo((FieldReference) src.getOperand()) ? fc.makeUpdatedNode(src) : src);\n                }\n                if(arg.getCode() == AstCode.GetStatic) {\n                    FieldReference fr = ((FieldReference) arg.getOperand());\n                    MemberInfo mi = new MemberInfo(fr);\n                    Expression prevExpr = target.fieldValues.get(mi);\n                    if(prevExpr != null)\n                        target = target.replaceField(fr, fc.makeUpdatedNode(prevExpr));\n                    return target.replaceAll(src -> src.getCode() == AstCode.GetStatic\n                        && fr.isEquivalentTo((FieldReference) src.getOperand()) ? fc.makeUpdatedNode(src) : src);\n                }\n                return target;\n            }\n            case InitObject:\n            case InvokeInterface:\n            case InvokeSpecial:\n            case InvokeStatic:\n            case InvokeVirtual: {\n                MethodReference mr = (MethodReference) expr.getOperand();\n                if (!fc.cf.isSideEffectFree(mr, expr.getCode() == AstCode.InitObject || expr.getCode() == AstCode.InvokeSpecial)) {\n                    target = target.replaceAll(src -> src.getCode() == AstCode.GetField || src.getCode() == AstCode.GetStatic\n                            || src.getCode() == AstCode.LoadElement ? fc.makeUpdatedNode(src) : src);\n                    // calling another constructor from current constructor will initialize all final fields\n                    if(expr.getCode() == AstCode.InvokeSpecial && fc.md.isConstructor() && mr.isConstructor() && \n                            Exprs.isThis(expr.getArguments().get(0)) && mr.getDeclaringType().isEquivalentTo(fc.md.getDeclaringType())) {\n                        Map<MemberInfo, Expression> ctorFields = fc.getCtorFields(mr);\n                        if(ctorFields != null) {\n                            if(!ctorFields.isEmpty()) {\n                                Map<MemberInfo,Expression> newFields = new HashMap<>(target.fieldValues);\n                                newFields.putAll(ctorFields);\n                                target = new Frame(target, target.sources, Maps.compactify(newFields));\n                            }\n                        }\n                        else\n                            target = target.deleteAllFields();\n                    }\n                    else\n                        target = target.deleteFields();\n                }\n                return target;\n            }\n            case StoreElement: {\n                return target.replaceAll(src -> src.getCode() == AstCode.LoadElement ? fc.makeUpdatedNode(src) : src);\n            }\n            case PutField: {\n                FieldReference fr = ((FieldReference) expr.getOperand());\n                if(fc.isThis(Exprs.getChild(expr, 0))) {\n                    target = target.replaceField(fr, Exprs.getChild(expr, 1));\n                }\n                return target.replaceAll(src -> src.getCode() == AstCode.GetField\n                    && fr.isEquivalentTo((FieldReference) src.getOperand()) ? fc.makeUpdatedNode(src) : src);\n            }\n            case PutStatic: {\n                FieldReference fr = ((FieldReference) expr.getOperand());\n                return target.replaceField(fr, Exprs.getChild(expr, 0)).replaceAll(src -> src\n                        .getCode() == AstCode.GetStatic && fr.isEquivalentTo((FieldReference) src.getOperand())\n                                ? fc.makeUpdatedNode(src) : src);\n            }\n            default:\n                return target;\n            }\n        }\n        @Override\n        public Frame transferExceptionalState(Frame src, Expression expr) {\n            return transferState(src, expr);\n        }\n        @Override\n        public TrueFalse<Frame> transferConditionalState(Frame src, Expression expr) {\n            return new TrueFalse<>(transferState(src, expr));\n        }\n        @Override\n        public Frame mergeStates(Frame s1, Frame s2) {\n            return s1.merge(s2, fc);\n        }\n        \n        @Override\n        public boolean sameState(Frame s1, Frame s2) {\n            return Frame.isEqual(s1, s2);\n        }\n\n        @Override\n        public Expression makeFact(Frame state, Expression expr) {\n            switch (expr.getCode()) {\n            case Load: {\n                Variable var = ((Variable) expr.getOperand());\n                return state.get(var);\n            }\n            case Inc:\n                if (expr.getOperand() instanceof Variable) {\n                    Variable var = ((Variable) expr.getOperand());\n                    Expression source = state.get(var);\n                    return source == null ? null : fc.makeUpdatedNode(source);\n                }\n                break;\n            case PostIncrement:\n            case PreIncrement: {\n                Expression arg = expr.getArguments().get(0);\n                if (arg.getOperand() instanceof Variable) {\n                    Variable var = ((Variable) arg.getOperand());\n                    Expression source = state.get(var);\n                    return source == null ? null : fc.makeUpdatedNode(source);\n                }\n                break;\n            }\n            case GetStatic: {\n                FieldReference fr = ((FieldReference) expr.getOperand());\n                return state.fieldValues.get(new MemberInfo(fr));\n            }\n            case GetField: {\n                FieldReference fr = ((FieldReference) expr.getOperand());\n                if(fc.isThis(Exprs.getChild(expr, 0))) {\n                    return state.fieldValues.get(new MemberInfo(fr));\n                }\n                break;\n            }\n            default:\n            }\n            return null;\n        }\n        \n        @Override\n        public Expression makeUnknownFact() {\n            return null;\n        }\n\n        @Override\n        public Expression mergeFacts(Expression f1, Expression f2) {\n            return makePhiNode(f1, f2, fc);\n        }\n        @Override\n        public boolean sameFact(Expression f1, Expression f2) {\n            return isExprEqual(f1, f2);\n        }\n\n        @Override\n        public void onSuccess(Frame exitState) {\n            fc.makeFieldsFrom(exitState);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/TrueFalse.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\n/**\n * @author lan\n *\n */\nclass TrueFalse<STATE> {\n    final STATE trueState, falseState;\n    \n    TrueFalse(STATE sameState) {\n        this(sameState, sameState);\n    }\n    \n    TrueFalse(STATE trueState, STATE falseState) {\n        this(trueState, falseState, false);\n    }\n\n    TrueFalse(STATE trueState, STATE falseState, boolean invert) {\n        this.trueState = invert ? falseState : trueState;\n        this.falseState = invert ? trueState : falseState;\n    }\n\n    public TrueFalse<STATE> invert() {\n        return new TrueFalse<>(falseState, trueState);\n    }\n    \n    @Override\n    public String toString() {\n        return \"TRUE: \"+trueState+\"\\nFALSE: \"+falseState;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/ValuesFlow.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.BinaryOperator;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.util.Types;\nimport com.strobel.assembler.metadata.BuiltinTypes;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class ValuesFlow {\n    public static List<Expression> annotate(Context ctx, MethodDefinition md, ClassFields cf, CFG cfg) {\n        ctx.incStat(\"ValuesFlow\");\n        Collection<Expression> origFrame = Inf.SOURCE.build(cf, cfg);\n        if(origFrame == null) {\n            ctx.incStat(\"Inf.SOURCE.Incomplete/ValuesFlow\");\n        }\n        if(!Inf.CONST.build(cfg)) {\n            ctx.incStat(\"Inf.CONST.Incomplete/ValuesFlow\");\n        }\n        if(!Inf.ETYPE.build(cfg)) {\n            ctx.incStat(\"Inf.ETYPE.Incomplete/ValuesFlow\");\n        }\n        if(!Inf.NULL.build(cfg)) {\n            ctx.incStat(\"Inf.NULL.Incomplete/ValuesFlow\");\n        }\n        cfg.forBodies((smd, smethod) -> Inf.PURITY.annotate(smethod, new FrameContext(smd, cf)));\n        cfg.forBodies((smd, smethod) -> Inf.BACKLINK.annotate(smethod));\n        return origFrame == null ? null : new ArrayList<>(origFrame);\n    }\n\n    public static <T> T reduce(Expression input, Function<Expression, T> mapper, BinaryOperator<T> reducer,\n            Predicate<T> pred) {\n        Expression source = getSource(input);\n        if (source.getCode() == AstCode.TernaryOp) {\n            T left = reduce(source.getArguments().get(1), mapper, reducer, pred);\n            if(pred.test(left))\n                return left;\n            T right = reduce(source.getArguments().get(2), mapper, reducer, pred);\n            return reducer.apply(left, right);\n        }\n        if (source.getCode() != SourceAnnotator.PHI_TYPE)\n            return mapper.apply(source);\n        boolean first = true;\n        T result = null;\n        for (Expression child : source.getArguments()) {\n            if (first) {\n                result = reduce(child, mapper, reducer, pred);\n                first = false;\n            } else {\n                result = reducer.apply(result, reduce(child, mapper, reducer, pred));\n            }\n            if(pred.test(result))\n                return result;\n        }\n        return result;\n    }\n    \n    public static TypeReference reduceType(Expression input) {\n        return reduce(input, e -> e.getCode() == AstCode.AConstNull ?\n                BuiltinTypes.Null : Types.getExpressionType(e), Types::mergeTypes, Objects::isNull);\n    }\n\n    public static Expression getSource(Expression input) {\n        Expression source = Inf.SOURCE.get(input);\n        return source == null ? input : source;\n    }\n    \n    public static boolean allMatch(Expression src, Predicate<Expression> pred) {\n        if(src.getCode() == SourceAnnotator.PHI_TYPE)\n            return src.getArguments().stream().allMatch(pred);\n        if(src.getCode() == AstCode.TernaryOp)\n            return allMatch(getSource(src.getArguments().get(1)), pred) &&\n                    allMatch(getSource(src.getArguments().get(2)), pred);\n        return pred.test(src);\n    }\n\n    public static boolean anyMatch(Expression src, Predicate<Expression> pred) {\n        if(src.getCode() == SourceAnnotator.PHI_TYPE)\n            return src.getArguments().stream().anyMatch(pred);\n        if(src.getCode() == AstCode.TernaryOp)\n            return anyMatch(getSource(src.getArguments().get(1)), pred) ||\n                    anyMatch(getSource(src.getArguments().get(2)), pred);\n        return pred.test(src);\n    }\n    \n    public static Expression findFirst(Expression src, Predicate<Expression> pred) {\n        if(src.getCode() == SourceAnnotator.PHI_TYPE)\n            return src.getArguments().stream().filter(pred).findFirst().orElse(null);\n        if(src.getCode() == AstCode.TernaryOp) {\n            Expression result = findFirst(getSource(src.getArguments().get(1)), pred);\n            return result == null ? findFirst(getSource(src.getArguments().get(2)), pred) : result;\n        }\n        return pred.test(src) ? src : null;\n    }\n\n    public static boolean hasPhiSource(Expression input) {\n        Expression source = Inf.SOURCE.get(input);\n        return source != null && source.getCode() == SourceAnnotator.PHI_TYPE;\n    }\n\n    public static boolean isSpecial(Expression expr) {\n        return expr.getCode() == SourceAnnotator.PHI_TYPE || expr.getCode() == SourceAnnotator.UPDATE_TYPE;\n    }\n\n    public static boolean hasUpdatedSource(Expression e) {\n        return ValuesFlow.getSource(e).getCode() == SourceAnnotator.UPDATE_TYPE;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/etype/AndType.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow.etype;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.flow.etype.SingleType.What;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.util.YesNoMaybe;\n\n/**\n * @author shustkost\n *\n */\npublic class AndType extends ComplexType {\n    AndType(Set<SingleType> types) {\n        super(types);\n    }\n    \n    static EType of(Set<SingleType> types) {\n        if(types.isEmpty())\n            return UNKNOWN;\n        if(types.size() == 1)\n            return types.iterator().next();\n        return new AndType(types);\n    }\n\n    @Override\n    public YesNoMaybe is(TypeReference tr, boolean exact) {\n        boolean hasYes = false, hasNo = false;\n        for (EType type : types) {\n            switch (type.is(tr, exact)) {\n            case YES:\n                hasYes = true;\n                break;\n            case NO:\n                hasNo = true;\n                break;\n            default:\n            }\n        }\n        if (hasYes && hasNo)\n            return YesNoMaybe.MAYBE;\n        if (hasYes)\n            return YesNoMaybe.YES;\n        if (hasNo)\n            return YesNoMaybe.NO;\n        return YesNoMaybe.MAYBE;\n    }\n\n    @Override\n    public YesNoMaybe isArray() {\n        boolean hasYes = false, hasNo = false;\n        for (EType type : types) {\n            switch (type.isArray()) {\n            case YES:\n                hasYes = true;\n                break;\n            case NO:\n                hasNo = true;\n                break;\n            default:\n            }\n        }\n        if (hasYes && hasNo)\n            return YesNoMaybe.MAYBE;\n        if (hasYes)\n            return YesNoMaybe.YES;\n        if (hasNo)\n            return YesNoMaybe.NO;\n        return YesNoMaybe.MAYBE;\n    }\n\n    @Override\n    public EType shrinkConstraint(TypeReference tr, boolean exact) {\n        Set<SingleType> yes = new HashSet<>(), no = new HashSet<>();\n        for (SingleType type : types) {\n            switch (type.is(tr, exact)) {\n            case YES:\n                yes.add(type);\n                break;\n            case NO:\n                no.add(type);\n                break;\n            default:\n            }\n        }\n        if (!yes.isEmpty() && !no.isEmpty())\n            return this;\n        if (!yes.isEmpty())\n            return of(yes);\n        if (!no.isEmpty())\n            return of(no);\n        return this;\n    }\n\n    @Override\n    public EType negate() {\n        Set<SingleType> newTypes = new HashSet<>();\n        for (SingleType type : types) {\n            EType neg = type.negate();\n            if (neg instanceof SingleType)\n                newTypes.add((SingleType) neg);\n            else if (neg == UNKNOWN) {\n                return UNKNOWN;\n            } else\n                throw new IllegalStateException(\"Unexpected type: \" + type);\n        }\n        return new OrType(newTypes);\n    }\n\n    @Override\n    EType reduce() {\n        SingleType result = null; \n        for(SingleType type : types) {\n            if(result == null)\n                result = type;\n            else {\n                if(result.what == What.EXACT) {\n                    if(type.what == What.EXACT)\n                        return UNKNOWN;\n                    continue;\n                }\n                if(type.what == What.EXACT) {\n                    result = type;\n                    continue;\n                }\n                if(result.what == What.SUBTYPE) {\n                    if(type.what == What.SUBTYPE && Types.isInstance(type.tr, result.tr))\n                        result = type;\n                    continue;\n                }\n                if(type.what == What.SUBTYPE) {\n                    result = type;\n                    continue;\n                }\n                return UNKNOWN;\n            }                \n        }\n        return result;\n    }\n    \n    @Override\n    EType append(SingleType st) {\n        if(types.contains(st))\n            return types.size() == 1 ? types.iterator().next() : this;\n        if(types.contains(st.negate()))\n            return UNKNOWN;\n        if(st.what == What.EXACT) {\n            if(types.stream().anyMatch(t -> t.is(st.tr, true) == YesNoMaybe.NO))\n                return UNKNOWN;\n            return st;\n        }\n        if(types.size() == 1) {\n            SingleType cur = types.iterator().next();\n            if(cur.what == What.EXACT) {\n                return st.is(cur.tr, true) == YesNoMaybe.NO ? UNKNOWN : cur;\n            }\n            if(cur.what == What.SUBTYPE && st.what == What.SUBTYPE) {\n                if(cur.is(st.tr, false) == YesNoMaybe.YES)\n                    return cur;\n                if(st.is(cur.tr, false) == YesNoMaybe.YES)\n                    return st;\n            }\n        }\n        Set<SingleType> newTypes = new HashSet<>(types);\n        newTypes.add(st);\n        return new AndType(newTypes);\n    }\n    \n    @Override\n    EType appendAny(EType type) {\n        if(type == UNKNOWN)\n            return types.size() == 1 ? types.iterator().next() : this;\n        if(type instanceof SingleType)\n            return append((SingleType) type);\n        if(type instanceof AndType) {\n            AndType result = (AndType) type;\n            for(SingleType t : types) {\n                EType newResult = result.append(t);\n                if(newResult == UNKNOWN)\n                    return UNKNOWN;\n                result = (AndType)newResult;\n            }\n            return result;\n        }\n        if(type instanceof ComplexType) {\n            return appendAny(((ComplexType)type).reduce());\n        }\n        return UNKNOWN;\n    }\n\n    @Override\n    public String toString() {\n        return toString(\" and \");\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/etype/ComplexType.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow.etype;\n\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * @author shustkost\n *\n */\nabstract class ComplexType implements EType {\n    Set<SingleType> types;\n\n    public ComplexType(Set<SingleType> types) {\n        this.types = types;\n    }\n\n    abstract EType reduce();\n\n    abstract EType append(SingleType st);\n    \n    abstract EType appendAny(EType type);\n    \n    @Override\n    public int hashCode() {\n        return types.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null || getClass() != obj.getClass())\n            return false;\n        return types.equals(((ComplexType) obj).types);\n    }\n\n    String toString(String delimiter) {\n        return types.stream().map(EType::toString).map(typeStr -> typeStr.contains(\" \") ? \"(\" + typeStr + \")\" : typeStr)\n                .sorted().collect(Collectors.joining(delimiter));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/etype/EType.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow.etype;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.flow.etype.SingleType.What;\nimport one.util.huntbugs.util.YesNoMaybe;\n\n/**\n * @author shustkost\n *\n */\npublic interface EType {\n    public static final EType UNKNOWN = new EType() {\n        @Override\n        public String toString() {\n            return \"??\";\n        }\n\n        @Override\n        public EType negate() {\n            return this;\n        }\n\n        @Override\n        public YesNoMaybe is(TypeReference tr, boolean exact) {\n            return YesNoMaybe.MAYBE;\n        }\n\n        @Override\n        public YesNoMaybe isArray() {\n            return YesNoMaybe.MAYBE;\n        }\n    };\n    \n    YesNoMaybe is(TypeReference tr, boolean exact);\n    \n    YesNoMaybe isArray();\n    \n    default EType shrinkConstraint(TypeReference tr, boolean exact) {\n        return this;\n    }\n\n    EType negate();\n\n    public static EType exact(TypeReference tr) {\n        return SingleType.of(tr, What.EXACT);\n    }\n\n    public static EType subType(TypeReference tr) {\n        return SingleType.of(tr, What.SUBTYPE);\n    }\n\n    public static EType or(EType t1, EType t2) {\n        if (t1 == null)\n            return t2;\n        if (t2 == null)\n            return t1;\n        if (t1 == UNKNOWN || t2 == UNKNOWN)\n            return UNKNOWN;\n        if (t1.equals(t2))\n            return t1;\n        if (t1 instanceof OrType) {\n            return ((OrType)t1).appendAny(t2);\n        }\n        if (t2 instanceof OrType) {\n            return ((OrType)t2).appendAny(t1);\n        }\n        if (t1 instanceof ComplexType) {\n            t1 = ((ComplexType)t1).reduce();\n            if(t1 == UNKNOWN)\n                return UNKNOWN;\n        }\n        if (t2 instanceof ComplexType) {\n            t2 = ((ComplexType)t2).reduce();\n            if(t2 == UNKNOWN)\n                return UNKNOWN;\n        }\n        SingleType st1 = (SingleType)t1;\n        SingleType st2 = (SingleType)t2;\n        return new OrType(Collections.singleton(st1)).append(st2);\n    }\n    \n    public static EType and(EType t1, EType t2) {\n        if(t1 == null || t1 == UNKNOWN)\n            return t2;\n        if(t2 == null || t2 == UNKNOWN)\n            return t1;\n        if(t1.equals(t2))\n            return t1;\n        if (t1 instanceof AndType) {\n            return ((AndType)t1).appendAny(t2);\n        }\n        if (t2 instanceof AndType) {\n            return ((AndType)t2).appendAny(t1);\n        }\n        if (t1 instanceof OrType) {\n            OrType ot1 = (OrType)t1;\n            if(ot1.types.contains(t2))\n                return ot1;\n            if(t2 instanceof OrType) {\n                Set<SingleType> commonTypes = new HashSet<>(ot1.types);\n                OrType ot2 = (OrType) t2;\n                commonTypes.retainAll(ot2.types);\n                if(!commonTypes.isEmpty()) {\n                    Set<SingleType> t1Types = new HashSet<>(ot1.types);\n                    t1Types.removeAll(commonTypes);\n                    if(t1Types.isEmpty())\n                        return t2;\n                    Set<SingleType> t2Types = new HashSet<>(ot2.types);\n                    t2Types.removeAll(commonTypes);\n                    if(t2Types.isEmpty())\n                        return t1;\n                    EType r1 = new OrType(t1Types).reduce();\n                    EType r2 = new OrType(t2Types).reduce();\n                    EType orend = r1 == UNKNOWN ? r2 : new AndType(Collections.singleton((SingleType)r1)).appendAny(r2);\n                    if(orend != UNKNOWN)\n                        return new OrType(commonTypes).appendAny(orend);\n                }\n            }\n            t1 = ot1.reduce();\n            if(t1 == UNKNOWN)\n                return t2;\n        }\n        if (t2 instanceof OrType) {\n            OrType ot2 = (OrType)t2;\n            if(ot2.types.contains(t1))\n                return ot2;\n            t2 = ot2.reduce();\n            if(t2 == UNKNOWN)\n                return t1; // TODO: restore t1\n        }\n        SingleType st1 = (SingleType)t1;\n        SingleType st2 = (SingleType)t2;\n        return new AndType(Collections.singleton(st1)).append(st2);\n    }\n\n    default EType unknownToNull() {\n        return this == UNKNOWN ? null : this;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/etype/OrType.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow.etype;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.flow.etype.SingleType.What;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.util.YesNoMaybe;\n\n/**\n * @author shustkost\n *\n */\npublic class OrType extends ComplexType {\n    OrType(Set<SingleType> types) {\n        super(types);\n    }\n\n    @Override\n    public YesNoMaybe is(TypeReference tr, boolean exact) {\n        boolean hasYes = false, hasNo = false, hasMaybe = false;\n        for (EType type : types) {\n            YesNoMaybe cur = type.is(tr, exact);\n            switch (cur) {\n            case YES:\n                hasYes = true;\n                break;\n            case NO:\n                hasNo = true;\n                break;\n            case MAYBE:\n                hasMaybe = true;\n                break;\n            default:\n                throw new InternalError();\n            }\n        }\n        if (hasMaybe || hasYes && hasNo)\n            return YesNoMaybe.MAYBE;\n        if (hasYes)\n            return YesNoMaybe.YES;\n        if (hasNo)\n            return YesNoMaybe.NO;\n        return YesNoMaybe.MAYBE;\n    }\n\n    @Override\n    public YesNoMaybe isArray() {\n        boolean hasYes = false, hasNo = false, hasMaybe = false;\n        for (EType type : types) {\n            YesNoMaybe cur = type.isArray();\n            switch (cur) {\n            case YES:\n                hasYes = true;\n                break;\n            case NO:\n                hasNo = true;\n                break;\n            case MAYBE:\n                hasMaybe = true;\n                break;\n            default:\n                throw new InternalError();\n            }\n        }\n        if (hasMaybe || hasYes && hasNo)\n            return YesNoMaybe.MAYBE;\n        if (hasYes)\n            return YesNoMaybe.YES;\n        if (hasNo)\n            return YesNoMaybe.NO;\n        return YesNoMaybe.MAYBE;\n    }\n    \n    @Override\n    public EType negate() {\n        Set<SingleType> newTypes = new HashSet<>();\n        for (SingleType type : types) {\n            EType neg = type.negate();\n            if (neg instanceof SingleType)\n                newTypes.add((SingleType) neg);\n            else if (neg != UNKNOWN)\n                throw new IllegalStateException(\"Unexpected type: \" + type);\n        }\n        return AndType.of(newTypes);\n    }\n\n    @Override\n    EType reduce() {\n        SingleType result = null; \n        for(SingleType type : types) {\n            if(result == null)\n                result = type;\n            else {\n                if(result.what.isNegative() || type.what.isNegative())\n                    return UNKNOWN;\n                EType newType = SingleType.of(Types.mergeTypes(result.tr, type.tr), SingleType.What.SUBTYPE);\n                if(!(newType instanceof SingleType))\n                    return UNKNOWN;\n                result = (SingleType) newType;\n            }\n        }\n        return result;\n    }\n\n    @Override\n    EType append(SingleType st) {\n        if(types.contains(st))\n            return types.size() == 1 ? types.iterator().next() : this;\n        if(types.contains(st.negate()))\n            return UNKNOWN;\n        if(st.what == What.EXACT) {\n            if(types.stream().anyMatch(t -> t.is(st.tr, true) == YesNoMaybe.YES))\n                return this;\n        }\n        Set<SingleType> newTypes = new HashSet<>(types);\n        newTypes.add(st);\n        return new OrType(newTypes);\n    }\n    \n    @Override\n    EType appendAny(EType type) {\n        if(type == UNKNOWN)\n            return UNKNOWN;\n        if(type instanceof SingleType)\n            return append((SingleType) type);\n        if(type instanceof OrType) {\n            OrType result = (OrType) type;\n            for(SingleType t : types) {\n                EType newResult = result.append(t);\n                if(newResult == UNKNOWN)\n                    return UNKNOWN;\n                result = (OrType)newResult;\n            }\n            return result;\n        }\n        if(type instanceof ComplexType) {\n            return appendAny(((ComplexType)type).reduce());\n        }\n        return UNKNOWN;\n    }\n\n    @Override\n    public String toString() {\n        return toString(\" or \");\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/flow/etype/SingleType.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.flow.etype;\n\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.util.YesNoMaybe;\n\n/**\n * @author shustkost\n *\n */\npublic class SingleType implements EType {\n    enum What {\n        EXACT, SUBTYPE, NOT, NOT_SUBTYPE;\n\n        What negate() {\n            switch (this) {\n            case EXACT:\n                return NOT;\n            case SUBTYPE:\n                return NOT_SUBTYPE;\n            case NOT:\n                return EXACT;\n            case NOT_SUBTYPE:\n                return SUBTYPE;\n            default:\n                throw new InternalError();\n            }\n        }\n\n        boolean isNegative() {\n            return this == NOT || this == NOT_SUBTYPE;\n        }\n    }\n\n    final TypeReference tr;\n    final boolean complete;\n    final What what;\n\n    static EType of(TypeReference tr, What what) {\n        if (tr == null || tr.isPrimitive() || (what == What.SUBTYPE && Types.isObject(tr)))\n            return UNKNOWN;\n        TypeDefinition td = tr.resolve();\n        if (td == null)\n            return UNKNOWN;\n        if (td.isFinal() || td.isPrimitive()) {\n            if (what == What.SUBTYPE)\n                what = What.EXACT;\n            if (what == What.NOT)\n                what = What.NOT_SUBTYPE;\n        }\n        TypeReference newTr = td;\n        while (tr.isArray()) {\n            tr = tr.getElementType();\n            newTr = newTr.makeArrayType();\n        }\n        boolean complete = Types.hasCompleteHierarchy(td);\n        return new SingleType(newTr, what, complete);\n    }\n\n    SingleType(TypeReference tr, What what, boolean complete) {\n        this.tr = tr;\n        this.what = what;\n        this.complete = complete;\n    }\n\n    @Override\n    public YesNoMaybe is(TypeReference other, boolean exact) {\n        switch (what) {\n        case EXACT:\n            if (exact)\n                return YesNoMaybe.of(tr.getInternalName().equals(other.getInternalName()));\n            if (Types.isInstance(tr, other))\n                return YesNoMaybe.YES;\n            if (!complete)\n                return YesNoMaybe.MAYBE;\n            return YesNoMaybe.NO;\n        case NOT:\n            if (exact)\n                return YesNoMaybe.of(!tr.getInternalName().equals(other.getInternalName()));\n            return YesNoMaybe.MAYBE;\n        case NOT_SUBTYPE:\n            return Types.isInstance(other, tr) ? YesNoMaybe.NO : YesNoMaybe.MAYBE;\n        case SUBTYPE: {\n            if (exact)\n                return Types.isInstance(other, tr) || !Types.hasCompleteHierarchy(other.resolve()) ? YesNoMaybe.MAYBE\n                        : YesNoMaybe.NO;\n            if (Types.isInstance(tr, other))\n                return YesNoMaybe.YES;\n            if (!complete || tr.resolve().isInterface())\n                return YesNoMaybe.MAYBE;\n            TypeDefinition superTd = other.resolve();\n            if (superTd == null || superTd.isInterface() || Types.isInstance(other, tr) || !Types.hasCompleteHierarchy(\n                superTd))\n                return YesNoMaybe.MAYBE;\n            return YesNoMaybe.NO;\n        }\n        default:\n            throw new InternalError();\n        }\n    }\n\n    @Override\n    public YesNoMaybe isArray() {\n        switch (what) {\n        case EXACT:\n            return YesNoMaybe.of(tr.isArray());\n        case NOT:\n            return YesNoMaybe.of(!tr.isArray());\n        case NOT_SUBTYPE:\n            return Types.isObject(tr) ? YesNoMaybe.NO : YesNoMaybe.MAYBE;\n        case SUBTYPE: {\n            if(tr.isArray())\n                return YesNoMaybe.YES;\n            if(Types.isObject(tr))\n                return YesNoMaybe.MAYBE;\n            return YesNoMaybe.NO;\n        }\n        default:\n            throw new InternalError();\n        }\n    }\n\n    @Override\n    public EType negate() {\n        return of(tr, what.negate());\n    }\n\n    @Override\n    public String toString() {\n        switch (what) {\n        case EXACT:\n            return tr.getFullName();\n        case NOT:\n            return \"not \" + tr.getFullName();\n        case NOT_SUBTYPE:\n            return \"not subtype of \" + tr.getFullName();\n        case SUBTYPE:\n            return \"subtype of \" + tr.getFullName();\n        default:\n            throw new InternalError();\n        }\n    }\n\n    @Override\n    public int hashCode() {\n        return tr.getInternalName().hashCode() * 31 + what.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null || getClass() != obj.getClass())\n            return false;\n        SingleType other = (SingleType) obj;\n        return what == other.what && tr.isEquivalentTo(other.tr);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/input/XmlReportReader.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.input;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.math.BigInteger;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.xml.sax.SAXException;\n\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.ErrorMessage;\nimport one.util.huntbugs.analysis.HuntBugsResult;\nimport one.util.huntbugs.util.Xml;\nimport one.util.huntbugs.warning.Messages;\nimport one.util.huntbugs.warning.Role.LocationRole;\nimport one.util.huntbugs.warning.Role.MemberRole;\nimport one.util.huntbugs.warning.Role.NumberRole;\nimport one.util.huntbugs.warning.Role.StringRole;\nimport one.util.huntbugs.warning.Role.TypeRole;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningStatus;\nimport one.util.huntbugs.warning.WarningAnnotation.Location;\nimport one.util.huntbugs.warning.WarningAnnotation.TypeInfo;\nimport one.util.huntbugs.warning.WarningType;\n\n/**\n * @author lan\n *\n */\npublic class XmlReportReader {\n    public static HuntBugsResult read(Context ctx, Path path) throws IOException, SAXException, ParserConfigurationException {\n        try (InputStream is = Files.newInputStream(path)) {\n            return read(ctx, is);\n        }\n    }\n\n    public static HuntBugsResult read(Context ctx, InputStream is) throws SAXException, IOException, ParserConfigurationException {\n        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n        DocumentBuilder builder = factory.newDocumentBuilder();\n        Document dom = builder.parse(is);\n        return read(ctx, dom);\n    }\n\n    public static HuntBugsResult read(Context ctx, Document dom) {\n        List<Warning> warnings = loadWarnings(dom, ctx);\n        List<ErrorMessage> errors = loadErrors(dom);\n        return new HuntBugsResult() {\n            @Override\n            public Stream<Warning> warnings() {\n                return warnings.stream();\n            }\n            \n            @Override\n            public Messages getMessages() {\n                return ctx.getMessages();\n            }\n            \n            @Override\n            public Stream<ErrorMessage> errors() {\n                return errors.stream();\n            }\n        };\n    }\n\n    private static List<ErrorMessage> loadErrors(Document dom) {\n        Element doc = dom.getDocumentElement();\n        Element errorList = Xml.getChild(doc, \"ErrorList\");\n        if(errorList == null)\n            return Collections.emptyList();\n        return Xml.elements(errorList).filter(e -> e.getTagName().equals(\"Error\"))\n            .map(XmlReportReader::loadError).collect(Collectors.toList());\n    }\n    \n    private static ErrorMessage loadError(Element e) {\n        String message = e.getTextContent();\n        String className = Xml.getAttribute(e, \"Class\");\n        String memberName = Xml.getAttribute(e, \"Member\");\n        String signature = Xml.getAttribute(e, \"Signature\");\n        String detector = Xml.getAttribute(e, \"Detector\");\n        int line = Xml.getIntAttribute(e, \"Line\", -1);\n        return new ErrorMessage(detector, className, memberName, signature, line, message);\n    }\n\n    private static List<Warning> loadWarnings(Document dom, Context ctx) {\n        Element doc = dom.getDocumentElement();\n        Element warningList = Xml.getChild(doc, \"WarningList\");\n        if(warningList == null)\n            return Collections.emptyList();\n        return Xml.elements(warningList).filter(e -> e.getTagName().equals(\"Warning\"))\n            .map(e -> loadWarning(e, ctx)).filter(Objects::nonNull).collect(Collectors.toList());\n    }\n    \n    private static Warning loadWarning(Element e, Context ctx) {\n        String type = Xml.getAttribute(e, \"Type\");\n        WarningType wtype = ctx.getWarningType(type);\n        if(wtype == null) {\n            return null;\n        }\n        int score = Xml.getIntAttribute(e, \"Score\", wtype.getMaxScore());\n        String status = Xml.getAttribute(e, \"Status\");\n        WarningStatus wstatus = WarningStatus.DEFAULT;\n        if(status != null) {\n            try {\n                wstatus = WarningStatus.valueOf(status.toUpperCase(Locale.ENGLISH));\n            } catch (IllegalArgumentException ex) {\n                // ignore\n            }\n        }\n        int priority = Math.max(0, wtype.getMaxScore()-score);\n        return new Warning(wtype, priority, Xml.elements(e).flatMap(XmlReportReader::loadAnnotations).collect(Collectors.toList()), wstatus);\n    }\n    \n    private static Stream<WarningAnnotation<?>> loadAnnotations(Element e) {\n        switch(e.getTagName()) {\n        case \"Class\": {\n            String file = Xml.getAttribute(e, \"SourceFile\");\n            WarningAnnotation<TypeInfo> type = Roles.TYPE.create(e.getAttribute(\"Name\"));\n            return file == null ? Stream.of(type)\n                    : Stream.of(type, Roles.FILE.create(file));\n        }\n        case \"Method\":\n            return Stream.of(Roles.METHOD.create(Xml.getChild((Element) e.getParentNode(), \"Class\")\n                    .getAttribute(\"Name\"), e.getAttribute(\"Name\"), e.getAttribute(\"Signature\")));\n        case \"Field\":\n            return Stream.of(Roles.FIELD.create(e.hasAttribute(\"Type\") ? e.getAttribute(\"Type\") : Xml.getChild(\n                (Element) e.getParentNode(), \"Class\").getAttribute(\"Name\"), e.getAttribute(\"Name\"), e\n                    .getAttribute(\"Signature\")));\n        case \"Location\":\n            return Stream.of(Roles.LOCATION.create(new Location(Xml.getIntAttribute(e, \"Offset\", -1), Xml\n                    .getIntAttribute(e, \"Line\", -1))));\n        case \"AnotherLocation\":\n            return Stream.of(Roles.ANOTHER_INSTANCE.create(new Location(Xml.getIntAttribute(e, \"Offset\", -1), Xml\n                .getIntAttribute(e, \"Line\", -1))));\n        case \"Annotation\":\n            return Stream.of(StringRole.forName(e.getAttribute(\"Role\")).create(e.getTextContent()));\n        case \"NumberAnnotation\": {\n            Number number = loadNumber(e.getAttribute(\"Type\"), e.getAttribute(\"Value\"));\n            return number == null ? null : Stream.of(NumberRole.forName(e.getAttribute(\"Role\")).create(number));\n        }\n        case \"TypeAnnotation\":\n            return Stream.of(TypeRole.forName(e.getAttribute(\"Role\")).create(e.getAttribute(\"Name\")));\n        case \"LocationAnnotation\":\n            return Stream.of(LocationRole.forName(e.getAttribute(\"Role\")).create(\n                new Location(Xml.getIntAttribute(e, \"Offset\", -1), Xml.getIntAttribute(e, \"Line\", -1))));\n        case \"MemberAnnotation\":\n            return Stream.of(MemberRole.forName(e.getAttribute(\"Role\")).create(e.getAttribute(\"Type\"),\n                e.getAttribute(\"Name\"), e.getAttribute(\"Signature\")));\n        default:\n            return null;\n        }\n    }\n\n    private static Number loadNumber(String type, String value) {\n        try {\n            switch(type) {\n            case \"Integer\":\n                return Integer.valueOf(value);\n            case \"Long\":\n                return Long.valueOf(value);\n            case \"Float\":\n                return Float.valueOf(value);\n            case \"Double\":\n                return Double.valueOf(value);\n            case \"BigInteger\":\n                return new BigInteger(value);\n            }\n        } catch (NumberFormatException e) {\n            // ignore\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/output/CombinedReportWriter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.output;\n\nimport java.util.List;\n\nimport org.w3c.dom.Document;\n\n/**\n * @author isopov\n *\n */\nclass CombinedReportWriter implements ReportWriter {\n\n    private final List<ReportWriter> writers;\n\n    public CombinedReportWriter(List<ReportWriter> writers) {\n        this.writers = writers;\n    }\n\n    @Override\n    public void write(Document dom) {\n        writers.forEach(writer -> writer.write(dom));\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/output/HtmlReportWriter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.output;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UncheckedIOException;\nimport java.io.Writer;\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerException;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.TransformerFactoryConfigurationError;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.transform.stream.StreamResult;\nimport javax.xml.transform.stream.StreamSource;\n\nimport org.w3c.dom.Document;\n\n/**\n * @author Tagir Valeev\n *\n */\nclass HtmlReportWriter implements ReportWriter {\n    private static final String XSL_PATH = \"huntbugs/report.xsl\";\n    private final Writer target;\n\n    public HtmlReportWriter(Writer target) {\n        this.target = target;\n\n    }\n\n    @Override\n    public void write(Document dom) {\n        try {\n            try (InputStream is = HtmlReportWriter.class.getClassLoader().getResourceAsStream(XSL_PATH)) {\n                StreamSource xsl = new StreamSource(is);\n                Transformer transformer = TransformerFactory.newInstance().newTransformer(xsl);\n                transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, \"yes\");\n                transformer.setOutputProperty(\"{http://xml.apache.org/xslt}indent-amount\", \"2\");\n                StreamResult result = new StreamResult(target);\n                transformer.transform(new DOMSource(dom), result);\n            } catch (IOException e) {\n                throw new UncheckedIOException(e);\n            }\n        } catch (TransformerFactoryConfigurationError | TransformerException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/output/ReportWriter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.output;\n\nimport org.w3c.dom.Document;\n\n/**\n * @author isopov\n *\n */\ninterface ReportWriter {\n    void write(Document dom);\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/output/Reports.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.output;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.io.Writer;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.ErrorMessage;\nimport one.util.huntbugs.analysis.HuntBugsResult;\nimport one.util.huntbugs.warning.Formatter;\nimport one.util.huntbugs.warning.Messages;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningStatus;\nimport one.util.huntbugs.warning.WarningAnnotation.Location;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\nimport one.util.huntbugs.warning.WarningAnnotation.TypeInfo;\n\n/**\n * @author isopov\n *\n */\npublic final class Reports {\n    /**\n     * Writes XML and/or HTML analysis reports\n     * \n     * @param xmlTarget path to the xml result (can be null if no xml output is\n     *        desired)\n     * @param htmlTarget path to the html result (can be null if no html output\n     *        is desired)\n     * @param result HuntBugs analysis result (usually {@link Context} object)\n     */\n    public static void write(Path xmlTarget, Path htmlTarget, HuntBugsResult result) {\n        Document dom = makeDom(result);\n        if (xmlTarget != null) {\n            try (Writer xmlWriter = Files.newBufferedWriter(xmlTarget)) {\n                new XmlReportWriter(xmlWriter).write(dom);\n            } catch (IOException e) {\n                throw new UncheckedIOException(e);\n            }\n        }\n        if (htmlTarget != null) {\n            try (Writer htmlWriter = Files.newBufferedWriter(htmlTarget)) {\n                new HtmlReportWriter(htmlWriter).write(dom);\n            } catch (IOException e) {\n                throw new UncheckedIOException(e);\n            }\n        }\n    }\n    \n    /**\n     * Merge several HuntBugs results into single\n     * \n     * @param results collection of results (at least one must be present)\n     * @return the merged result\n     */\n    public static HuntBugsResult merge(Collection<HuntBugsResult> results) {\n        if(results.isEmpty())\n            throw new IllegalArgumentException(\"Result should not be empty\");\n        HuntBugsResult first = results.iterator().next();\n        \n        return new HuntBugsResult() {\n            @Override\n            public Stream<Warning> warnings() {\n                return results.stream().flatMap(HuntBugsResult::warnings);\n            }\n            \n            @Override\n            public Messages getMessages() {\n                return first.getMessages();\n            }\n            \n            @Override\n            public Stream<ErrorMessage> errors() {\n                return results.stream().flatMap(HuntBugsResult::errors);\n            }\n        };\n    }\n    \n    public static HuntBugsResult diff(HuntBugsResult oldResult, HuntBugsResult newResult) {\n        List<Warning> diffWarnings = diffWarnings(oldResult.warnings().filter(w -> w.getStatus() != WarningStatus.FIXED)\n                .collect(Collectors.toList()), newResult.warnings().collect(Collectors.toList()));\n        return new HuntBugsResult() {\n            @Override\n            public Stream<Warning> warnings() {\n                return diffWarnings.stream();\n            }\n            \n            @Override\n            public Messages getMessages() {\n                return newResult.getMessages();\n            }\n            \n            @Override\n            public Stream<ErrorMessage> errors() {\n                return newResult.errors();\n            }\n        };\n    }\n\n    private static List<Warning> diffWarnings(List<Warning> oldWarnings, List<Warning> newWarnings) {\n        Function<Warning, List<Object>> keyExtractor = w ->\n            Arrays.asList(w.getType().getName(), w.getAnnotation(Roles.TYPE), w.getAnnotation(Roles.METHOD),\n                w.getAnnotation(Roles.FIELD), w.getAnnotation(Roles.VARIABLE));\n        Map<List<Object>, List<Warning>> oldWarningsMap = oldWarnings.stream().collect(Collectors.groupingBy(keyExtractor));\n        List<Warning> result = new ArrayList<>();\n        for(Warning warn : newWarnings) {\n            List<Object> key = keyExtractor.apply(warn);\n            List<Warning> matchedList = oldWarningsMap.get(key);\n            Warning matched = null;\n            if(matchedList != null) {\n                if(matchedList.size() == 1) {\n                    oldWarningsMap.remove(key);\n                    matched = matchedList.get(0);\n                } else {\n                    matched = matchedList.stream().max(Comparator.comparingInt(w -> matchScore(warn, w))).get();\n                    matchedList.remove(matched);\n                }\n            }\n            WarningStatus status = WarningStatus.DEFAULT;\n            if(matched == null) {\n                status = WarningStatus.ADDED;\n            } else {\n                Set<WarningAnnotation<?>> waNew = warn.annotations().filter(\n                    wa -> wa.getRole().getType() != Location.class).collect(Collectors.toSet());\n                Set<WarningAnnotation<?>> waOld = matched.annotations().filter(\n                    wa -> wa.getRole().getType() != Location.class).collect(Collectors.toSet());\n                if(!waOld.equals(waNew)) {\n                    status = WarningStatus.CHANGED;\n                } else if(warn.getScore() > matched.getScore()) {\n                    status = WarningStatus.SCORE_RAISED;\n                } else if(warn.getScore() < matched.getScore()) {\n                    status = WarningStatus.SCORE_LOWERED;\n                }\n            }\n            result.add(warn.withStatus(status));\n        }\n        oldWarningsMap.values().stream().flatMap(List::stream).map(w -> w.withStatus(WarningStatus.FIXED)).forEach(result::add);\n        return result;\n    }\n    \n    private static int matchScore(Warning w1, Warning w2) {\n        int penalty = w1.getScore() == w2.getScore() ? 0 : 2;\n        if(w1.annotations().collect(Collectors.toSet()).equals(w2.annotations().collect(Collectors.toSet()))) {\n            return 100 - penalty;\n        }\n        if (w1.annotations().filter(wa -> wa.getRole().getType() != Location.class).collect(Collectors.toSet()).equals(\n            w2.annotations().filter(wa -> wa.getRole().getType() != Location.class).collect(Collectors.toSet()))) {\n            return 50 - penalty;\n        }\n        return 10 - penalty;\n    }\n\n    private static Document makeDom(HuntBugsResult ctx) {\n        Document doc;\n        try {\n            doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();\n        } catch (ParserConfigurationException e) {\n            throw new RuntimeException(e);\n        }\n        Element root = doc.createElement(\"HuntBugs\");\n        Element errors = doc.createElement(\"ErrorList\");\n        ctx.errors().map(e -> writeError(doc, e)).forEach(errors::appendChild);\n        if (errors.hasChildNodes())\n            root.appendChild(errors);\n        Element warnings = doc.createElement(\"WarningList\");\n        Formatter formatter = new Formatter(ctx.getMessages());\n        ctx.warnings().sorted(\n            Comparator.comparing(Warning::getScore).reversed().thenComparing(w -> w.getType().getName()).thenComparing(\n                Warning::getClassName)).map(w -> writeWarning(doc, w, formatter)).forEach(warnings::appendChild);\n        root.appendChild(warnings);\n        doc.appendChild(root);\n        return doc;\n    }\n\n    private static Element writeError(Document doc, ErrorMessage e) {\n        Element element = doc.createElement(\"Error\");\n        if (e.getDetector() != null)\n            element.setAttribute(\"Detector\", e.getDetector());\n        if (e.getClassName() != null)\n            element.setAttribute(\"Class\", e.getClassName());\n        if (e.getElementName() != null)\n            element.setAttribute(\"Member\", e.getElementName());\n        if (e.getDescriptor() != null)\n            element.setAttribute(\"Signature\", e.getDescriptor());\n        if (e.getLine() != -1)\n            element.setAttribute(\"Line\", String.valueOf(e.getLine()));\n        element.appendChild(doc.createCDATASection(e.getError()));\n        return element;\n    }\n\n    private static Element writeWarning(Document doc, Warning w, Formatter formatter) {\n        Element element = doc.createElement(\"Warning\");\n        element.setAttribute(\"Type\", w.getType().getName());\n        element.setAttribute(\"Category\", w.getType().getCategory());\n        element.setAttribute(\"Score\", String.valueOf(w.getScore()));\n        element.setAttribute(\"Status\", w.getStatus().name().toLowerCase(Locale.ENGLISH));\n        Element title = doc.createElement(\"Title\");\n        title.appendChild(doc.createTextNode(formatter.getTitle(w)));\n        element.appendChild(title);\n        Element description = doc.createElement(\"Description\");\n        description.appendChild(doc.createTextNode(formatter.getDescription(w)));\n        element.appendChild(description);\n        Element longDescription = doc.createElement(\"LongDescription\");\n        longDescription.appendChild(doc.createCDATASection(formatter.getLongDescription(w)));\n        element.appendChild(longDescription);\n        Element classElement = doc.createElement(\"Class\");\n        Element methodElement = doc.createElement(\"Method\");\n        Element fieldElement = doc.createElement(\"Field\");\n        element.appendChild(classElement);\n        Element location = doc.createElement(\"Location\");\n        List<Element> anotherLocations = new ArrayList<>();\n        List<Element> attributes = new ArrayList<>();\n        w.annotations().forEach(\n            anno -> {\n                switch (anno.getRole().toString()) {\n                case \"TYPE\":\n                    classElement.setAttribute(\"Name\", ((TypeInfo) anno.getValue()).getTypeName());\n                    break;\n                case \"FILE\":\n                    classElement.setAttribute(\"SourceFile\", Formatter.formatValue(anno.getValue(),\n                        Formatter.FORMAT_PLAIN));\n                    break;\n                case \"LOCATION\": {\n                    location.setAttribute(\"Offset\", String.valueOf(((Location) anno.getValue()).getOffset()));\n                    int line = ((Location) anno.getValue()).getSourceLine();\n                    if (line != -1)\n                        location.setAttribute(\"Line\", String.valueOf(line));\n                    break;\n                }\n                case \"ANOTHER_INSTANCE\": {\n                    Element anotherLocation = doc.createElement(\"AnotherLocation\");\n                    anotherLocation.setAttribute(\"Offset\", String.valueOf(((Location) anno.getValue()).getOffset()));\n                    int line = ((Location) anno.getValue()).getSourceLine();\n                    if (line != -1)\n                        anotherLocation.setAttribute(\"Line\", String.valueOf(line));\n                    anotherLocations.add(anotherLocation);\n                    break;\n                }\n                case \"METHOD\": {\n                    MemberInfo mr = (MemberInfo) anno.getValue();\n                    methodElement.setAttribute(\"Name\", mr.getName());\n                    methodElement.setAttribute(\"Signature\", mr.getSignature());\n                    break;\n                }\n                case \"FIELD\": {\n                    MemberInfo mr = (MemberInfo) anno.getValue();\n                    fieldElement.setAttribute(\"Type\", mr.getTypeName());\n                    fieldElement.setAttribute(\"Name\", mr.getName());\n                    fieldElement.setAttribute(\"Signature\", mr.getSignature());\n                    break;\n                }\n                default:\n                    Object value = anno.getValue();\n                    Element attribute;\n                    if (value instanceof TypeInfo) {\n                        attribute = doc.createElement(\"TypeAnnotation\");\n                        attribute.setAttribute(\"Name\", ((TypeInfo) value).getTypeName());\n                    } else if (value instanceof Location) {\n                        attribute = doc.createElement(\"LocationAnnotation\");\n                        attribute.setAttribute(\"Line\", String.valueOf(((Location) value).getSourceLine()));\n                        attribute.setAttribute(\"Offset\", String.valueOf(((Location) value).getOffset()));\n                    } else if (value instanceof MemberInfo) {\n                        MemberInfo mr = (MemberInfo) anno.getValue();\n                        attribute = doc.createElement(\"MemberAnnotation\");\n                        attribute.setAttribute(\"Type\", mr.getTypeName());\n                        attribute.setAttribute(\"Name\", mr.getName());\n                        attribute.setAttribute(\"Signature\", mr.getSignature());\n                    } else if (value instanceof Number) {\n                        Number n = (Number) anno.getValue();\n                        attribute = doc.createElement(\"NumberAnnotation\");\n                        attribute.setAttribute(\"Type\", n.getClass().getSimpleName());\n                        attribute.setAttribute(\"Value\", n.toString());\n                    } else {\n                        attribute = doc.createElement(\"Annotation\");\n                        attribute.appendChild(doc.createTextNode(Formatter.formatValue(anno.getValue(),\n                            Formatter.FORMAT_PLAIN)));\n                    }\n                    attribute.setAttribute(\"Role\", anno.getRole().toString());\n                    attributes.add(attribute);\n                }\n            });\n        if (methodElement.hasAttribute(\"Name\"))\n            element.appendChild(methodElement);\n        if (fieldElement.hasAttribute(\"Name\"))\n            element.appendChild(fieldElement);\n        if (location.hasAttribute(\"Offset\")) {\n            if (classElement.hasAttribute(\"SourceFile\"))\n                location.setAttribute(\"SourceFile\", classElement.getAttribute(\"SourceFile\"));\n            element.appendChild(location);\n        }\n        anotherLocations.forEach(al -> {\n            if (classElement.hasAttribute(\"SourceFile\"))\n                al.setAttribute(\"SourceFile\", classElement.getAttribute(\"SourceFile\"));\n            element.appendChild(al);\n        });\n        attributes.forEach(element::appendChild);\n        return element;\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/output/XmlReportWriter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.output;\n\nimport java.io.Writer;\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerException;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.TransformerFactoryConfigurationError;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.transform.stream.StreamResult;\nimport org.w3c.dom.Document;\n\n/**\n * @author Tagir Valeev\n *\n */\nclass XmlReportWriter implements ReportWriter {\n    private final Writer target;\n\n    public XmlReportWriter(Writer target) {\n        this.target = target;\n    }\n\n    @Override\n    public void write(Document dom) {\n        try {\n            Transformer transformer = TransformerFactory.newInstance().newTransformer();\n            DOMSource source = new DOMSource(dom);\n\n            StreamResult result = new StreamResult(target);\n            transformer.setOutputProperty(javax.xml.transform.OutputKeys.MEDIA_TYPE, \"text/xml\");\n            transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, \"yes\");\n            transformer.setOutputProperty(\"{http://xml.apache.org/xslt}indent-amount\", \"2\");\n            transformer.transform(source, result);\n        } catch (TransformerFactoryConfigurationError | TransformerException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/AbstractTypeDatabase.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic abstract class AbstractTypeDatabase<E> {\n    private final Map<String, E> map = new HashMap<>();\n    private final Function<String, E> fn;\n    \n    protected AbstractTypeDatabase(Function<String, E> elementSupplier) {\n        this.fn = Objects.requireNonNull(elementSupplier);\n    }\n    \n    protected void visitType(TypeDefinition td) {\n        // Default implementation is empty, should be subclasses\n    }\n    \n    protected E getOrCreate(TypeReference ref) {\n        return map.computeIfAbsent(ref.getInternalName(), fn);\n    }\n    \n    protected E getOrCreate(String internalName) {\n        return map.computeIfAbsent(internalName, fn);\n    }\n    \n    public E get(TypeReference ref) {\n        return map.get(ref.getInternalName());\n    }\n    \n    public E get(String internalName) {\n        return map.get(internalName);\n    }\n    \n    @Override\n    public String toString() {\n        return \"Database <\"+getClass().getName()+\">\";\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/ClassContext.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport java.lang.invoke.MethodHandle;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.ErrorMessage;\nimport one.util.huntbugs.assertions.MemberAsserter;\nimport one.util.huntbugs.util.Types;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\nimport one.util.huntbugs.warning.WarningType;\n\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class ClassContext extends ElementContext {\n    final TypeDefinition type;\n    final Object det;\n    final ClassData cdata;\n    List<WarningAnnotation<?>> annot;\n\n    ClassContext(Context ctx, ClassData cdata, Detector detector) {\n        super(ctx, detector);\n        this.type = cdata.td;\n        this.cdata = cdata;\n        this.det = detector.newInstance();\n    }\n    \n    List<WarningAnnotation<?>> getTypeSpecificAnnotations() {\n        if(annot == null) {\n            annot = new ArrayList<>();\n            annot.add(Roles.TYPE.create(type));\n            String sourceFile = Types.getSourceFile(type);\n            if(sourceFile != null)\n                annot.add(Roles.FILE.create(sourceFile));\n        }\n        return annot;\n    }\n    \n    boolean visitClass() {\n        for(MethodHandle mh : detector.classVisitors) {\n            try {\n                if (!(boolean) detector.bindDatabases(Detector.CLASS_VISITOR_TYPE.parameterCount(), type, mh)\n                        .invoke(det, this, type)) {\n                    return false;\n                }\n            } catch (Throwable e) {\n                ctx.addError(new ErrorMessage(detector, type, e));\n            }\n        }\n        return !detector.methodVisitors.isEmpty() || !detector.astVisitors.isEmpty()\n            || !detector.methodAfterVisitors.isEmpty() || !detector.classAfterVisitors.isEmpty()\n            || !detector.fieldVisitors.isEmpty();\n    }\n    \n    void visitAfterClass() {\n        for(MethodHandle mh : detector.classAfterVisitors) {\n            try {\n                detector.bindDatabases(Detector.CLASS_VISITOR_TYPE.parameterCount(), type, mh).invoke(det, this, type);\n            } catch (Throwable e) {\n                ctx.addError(new ErrorMessage(detector, type, e));\n            }\n        }\n    }\n\n    public void report(String warning, int priority, WarningAnnotation<?>... annotations) {\n        WarningType wt = resolveWarningType(warning, priority);\n        if(wt == null)\n            return;\n        List<WarningAnnotation<?>> anno = new ArrayList<>();\n        anno.addAll(getTypeSpecificAnnotations());\n        anno.addAll(Arrays.asList(annotations));\n        Warning w = new Warning(wt, priority, anno);\n        if(!cdata.filter.test(w))\n            return;\n        if(cdata.hasAsserters()) {\n            MemberAsserter ma = cdata.ca;\n            MemberInfo mi = w.getAnnotation(Roles.METHOD);\n            if(mi != null)\n                ma = cdata.getAsserter(mi);\n            ma.checkWarning(this::error, w);\n        }\n        ctx.addWarning(w);\n    }\n\n    @Override\n    public void error(String message) {\n        ctx.addError(new ErrorMessage(detector, type, message));\n    }\n\n    MethodContext forMethod(MethodData md) {\n        return new MethodContext(ctx, this, md);\n    }\n    \n    FieldContext forField(FieldData fd) {\n        return new FieldContext(ctx, this, fd);\n    }\n    \n    MemberAsserter getMemberAsserter(MemberReference mr) {\n        return cdata.getAsserter(mr);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/ClassData.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.ErrorMessage;\nimport one.util.huntbugs.assertions.MemberAsserter;\nimport one.util.huntbugs.filter.AnnotationFilters;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class ClassData {\n    final TypeDefinition td;\n    final MemberAsserter ca;\n    Map<MemberInfo, MemberAsserter> mas;\n    Predicate<Warning> filter;\n\n    ClassData(TypeDefinition td) {\n        this.td = td;\n        this.ca = MemberAsserter.forMember(td);\n        this.filter = AnnotationFilters.forType(td);\n    }\n\n    void finish(Context ctx) {\n        ca.checkFinally(err -> ctx.addError(new ErrorMessage(null, td, err)));\n        if (mas != null) {\n            mas.forEach((mi, ma) -> ma.checkFinally(err -> ctx.addError(new ErrorMessage(null, mi.getTypeName(), mi\n                    .getName(), mi.getSignature(), -1, err))));\n        }\n    }\n\n    MemberAsserter getAsserter(MemberReference mr) {\n        if (mas == null)\n            return ca;\n        return mas.getOrDefault(new MemberInfo(mr), ca);\n    }\n\n    MemberAsserter getAsserter(MemberInfo mi) {\n        if (mas == null)\n            return ca;\n        return mas.getOrDefault(mi, ca);\n    }\n    \n    MemberAsserter registerAsserter(MemberReference mr) {\n        MemberAsserter ma = MemberAsserter.forMember(ca, mr);\n        if (!ma.isEmpty()) {\n            if (mas == null)\n                mas = new HashMap<>();\n            if (mas.put(new MemberInfo(mr), ma) != null) {\n                throw new InternalError(\"Asserter is registered twice for \" + mr);\n            }\n        }\n        return ma;\n    }\n    \n    boolean hasAsserters() {\n        return mas != null || !ca.isEmpty();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/DatabaseRegistry.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.registry.anno.TypeDatabase;\nimport one.util.huntbugs.registry.anno.TypeDatabaseItem;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class DatabaseRegistry {\n    Context ctx;\n    Map<Class<?>, DatabaseInfo<?>> instances = new HashMap<>();\n\n    static class DatabaseInfo<T> {\n        final T db;\n        final AbstractTypeDatabase<T> parentDb;\n\n        public DatabaseInfo(T db, AbstractTypeDatabase<T> parentDb) {\n            this.db = db;\n            this.parentDb = parentDb;\n        }\n\n        public T getDatabase(TypeReference tr) {\n            if (db != null)\n                return db;\n            return parentDb.get(tr);\n        }\n    }\n\n    public DatabaseRegistry(Context ctx) {\n        super();\n        this.ctx = ctx;\n    }\n\n    public <T> Function<TypeReference, T> queryDatabase(Class<T> clazz) {\n        return getDatabaseInfo(clazz)::getDatabase;\n    }\n    \n    void visitType(TypeDefinition td) {\n        for(DatabaseInfo<?> dbi : instances.values()) {\n            Object db = dbi.db;\n            if(db instanceof AbstractTypeDatabase) {\n                ((AbstractTypeDatabase<?>) db).visitType(td);\n            }\n        }\n    }\n\n    private <T> DatabaseInfo<T> getDatabaseInfo(Class<T> clazz) {\n        // Cannot use computeIfAbsent here as recursive update is unsafe\n        @SuppressWarnings(\"unchecked\")\n        DatabaseInfo<T> di = (DatabaseInfo<T>) instances.get(clazz);\n        if(di == null) {\n            di = resolveDatabase(clazz);\n            instances.put(clazz, di);\n        }\n        return di;\n    }\n\n    private <T> DatabaseInfo<T> resolveDatabase(Class<T> clazz) {\n        ctx.incStat(\"Databases\");\n        TypeDatabase td = clazz.getAnnotation(TypeDatabase.class);\n        TypeDatabaseItem tdi = clazz.getAnnotation(TypeDatabaseItem.class);\n        if (td != null && tdi != null) {\n            throw new InternalError(\"Database \" + clazz + \" is annotated both as \" + TypeDatabase.class.getSimpleName()\n                + \" and \" + TypeDatabaseItem.class.getSimpleName() + \". Remove one of the annotations!\");\n        }\n        if (td == null && tdi == null) {\n            throw new IllegalStateException(\"Unknown database requested: \" + clazz + \" (not annotated as \"\n                + TypeDatabase.class.getSimpleName() + \" or \" + TypeDatabaseItem.class.getSimpleName()+\")\");\n        }\n        if (td != null) {\n            try {\n                return new DatabaseInfo<>(clazz.newInstance(), null);\n            } catch (InstantiationException | IllegalAccessException e) {\n                throw new IllegalStateException(\"Unable to instantiate database \" + clazz, e);\n            }\n        }\n        @SuppressWarnings(\"unchecked\")\n        DatabaseInfo<? extends AbstractTypeDatabase<T>> parentInfo = getDatabaseInfo((Class<AbstractTypeDatabase<T>>) tdi\n                .parentDatabase());\n        return new DatabaseInfo<>(null, parentInfo.db);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/Detector.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.invoke.MethodType;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.anno.AstNodes;\nimport one.util.huntbugs.registry.anno.AstVisitor;\nimport one.util.huntbugs.registry.anno.ClassVisitor;\nimport one.util.huntbugs.registry.anno.FieldVisitor;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.VisitOrder;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.warning.WarningType;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class Detector {\n    static final MethodType METHOD_VISITOR_TYPE = MethodType.methodType(boolean.class, Object.class,\n        MethodContext.class, MethodDefinition.class, TypeDefinition.class);\n    static final MethodType FIELD_VISITOR_TYPE = MethodType.methodType(void.class, Object.class, FieldContext.class,\n        FieldDefinition.class, TypeDefinition.class);\n    static final MethodType CLASS_VISITOR_TYPE = MethodType.methodType(boolean.class, Object.class, ClassContext.class,\n        TypeDefinition.class);\n    private static final MethodHandle ALWAYS_TRUE = MethodHandles.constant(boolean.class, true);\n    private static final MethodType NODE_VISITOR_TYPE = MethodType.methodType(boolean.class, Object.class, Node.class,\n        NodeChain.class, MethodContext.class, MethodDefinition.class, TypeDefinition.class);\n\n    private final Map<String, WarningType> wts;\n    final Map<Class<?>, Function<TypeReference, ?>> dbFetchers = new HashMap<>();\n    private final Class<?> clazz;\n    final List<VisitorInfo> astVisitors = new ArrayList<>();\n    final List<MethodHandle> fieldVisitors = new ArrayList<>();\n    final List<MethodHandle> methodVisitors = new ArrayList<>();\n    final List<MethodHandle> methodAfterVisitors = new ArrayList<>();\n    final List<MethodHandle> classVisitors = new ArrayList<>();\n    final List<MethodHandle> classAfterVisitors = new ArrayList<>();\n\n    class VisitorInfo {\n        final VisitorType type;\n        final MethodHandle mh;\n        final AstVisitor anno;\n\n        public VisitorInfo(AstVisitor anno, VisitorType type, MethodHandle mh) {\n            this.anno = anno;\n            this.type = type;\n            this.mh = mh;\n        }\n\n        public MethodHandle bind(TypeDefinition td) {\n            MethodHandle mh = this.mh;\n            mh = bindDatabases(type.wantedType.parameterCount(), td, mh);\n            return type.adapt(mh);\n        }\n\n        public boolean isApplicable(MethodDefinition md) {\n            if (!anno.methodName().isEmpty() && !anno.methodName().equals(md.getName()))\n                return false;\n            if (!anno.methodSignature().isEmpty() && !anno.methodSignature().equals(md.getSignature()))\n                return false;\n            if (anno.minVersion() > 0 && md.getDeclaringType().getCompilerMajorVersion() - 44 < anno.minVersion())\n                return false;\n            return true;\n        }\n    }\n\n    static enum VisitorType {\n        // All\n        AST_NODE_VISITOR(AstNodes.ALL, MethodType.methodType(boolean.class, Object.class, Node.class, NodeChain.class,\n            MethodContext.class, MethodDefinition.class, TypeDefinition.class), null),\n        // Expressions\n        AST_EXPRESSION_VISITOR(AstNodes.EXPRESSIONS, MethodType.methodType(boolean.class, Object.class,\n            Expression.class, NodeChain.class, MethodContext.class, MethodDefinition.class, TypeDefinition.class),\n                \"runExpression\"),\n        // Root\n        AST_BODY_VISITOR(AstNodes.ROOT, MethodType.methodType(void.class, Object.class, Block.class,\n            MethodContext.class, MethodDefinition.class, TypeDefinition.class), \"runBody\");\n\n        AstNodes nodeTypes;\n        MethodType wantedType;\n        private MethodHandle adapter;\n\n        static boolean runBody(MethodHandle mh, Object det, Node n, NodeChain nc, MethodContext mc,\n                MethodDefinition md, TypeDefinition td) throws Throwable {\n            if (nc == null) {\n                mh.invokeExact(det, (Block) n, mc, md, td);\n            }\n            return true;\n        }\n\n        static boolean runExpression(MethodHandle mh, Object det, Node n, NodeChain nc, MethodContext mc,\n                MethodDefinition md, TypeDefinition td) throws Throwable {\n            if (n instanceof Expression) {\n                return (boolean) mh.invokeExact(det, (Expression) n, nc, mc, md, td);\n            }\n            return true;\n        }\n\n        private VisitorType(AstNodes nodeTypes, MethodType wantedType, String adapter) {\n            this.nodeTypes = nodeTypes;\n            this.wantedType = wantedType;\n            if (adapter != null) {\n                try {\n                    this.adapter = MethodHandles.lookup().findStatic(getClass(), adapter,\n                        NODE_VISITOR_TYPE.insertParameterTypes(0, MethodHandle.class));\n                } catch (NoSuchMethodException | IllegalAccessException e) {\n                    throw new InternalError(e);\n                }\n            }\n        }\n\n        MethodHandle adapt(MethodHandle mh) {\n            if (adapter == null)\n                return mh;\n            return adapter.bindTo(mh);\n        }\n    }\n\n    public Detector(Map<String, WarningType> wts, Class<?> clazz, DatabaseRegistry databases)\n            throws IllegalAccessException {\n        this.wts = Objects.requireNonNull(wts);\n        this.clazz = Objects.requireNonNull(clazz);\n        for (Method m : clazz.getMethods()) {\n            AstVisitor av = m.getAnnotation(AstVisitor.class);\n            if (av != null) {\n                for (VisitorType type : VisitorType.values()) {\n                    if (av.nodes() == type.nodeTypes) {\n                        astVisitors.add(new VisitorInfo(av, type, adapt(m, type.wantedType, databases)));\n                    }\n                }\n            }\n            MethodVisitor mv = m.getAnnotation(MethodVisitor.class);\n            if (mv != null) {\n                (mv.order() == VisitOrder.AFTER ? methodAfterVisitors : methodVisitors).add(adapt(m,\n                    METHOD_VISITOR_TYPE, databases));\n            }\n            FieldVisitor fv = m.getAnnotation(FieldVisitor.class);\n            if (fv != null) {\n                fieldVisitors.add(adapt(m, FIELD_VISITOR_TYPE, databases));\n            }\n            ClassVisitor cv = m.getAnnotation(ClassVisitor.class);\n            if (cv != null) {\n                (cv.order() == VisitOrder.AFTER ? classAfterVisitors : classVisitors).add(adapt(m, CLASS_VISITOR_TYPE,\n                    databases));\n            }\n        }\n    }\n\n    MethodHandle bindDatabases(int count, TypeDefinition td, MethodHandle mh) {\n        int curCount = mh.type().parameterCount();\n        if (curCount > count) {\n            Object[] params = new Object[curCount - count];\n            for (int i = count; i < curCount; i++) {\n                params[i - count] = getDatabase(mh.type().parameterType(i), td);\n            }\n            mh = MethodHandles.insertArguments(mh, count, params);\n        }\n        return mh;\n    }\n\n    private <T> T getDatabase(Class<T> clazz, TypeReference tr) {\n        Function<TypeReference, ?> fn = dbFetchers.get(clazz);\n        if (fn == null)\n            throw new IllegalArgumentException(\"Requested unknown database: \" + clazz);\n        return clazz.cast(fn.apply(tr));\n    }\n\n    private MethodHandle adapt(Method method, MethodType wantedType, DatabaseRegistry databases)\n            throws IllegalAccessException {\n        MethodHandle mh = MethodHandles.publicLookup().unreflect(method);\n        MethodType type = mh.type();\n        MethodHandle result = MethodHandles.explicitCastArguments(mh, type.changeParameterType(0, Object.class));\n        if (wantedType.returnType() == boolean.class) {\n            if (type.returnType() == void.class) {\n                result = MethodHandles.filterReturnValue(result, ALWAYS_TRUE);\n                type = mh.type();\n            } else if (type.returnType() != boolean.class) {\n                throw new IllegalStateException(mh + \": Unexpected return type \" + type.returnType());\n            }\n        } else {\n            if (type.returnType() != wantedType.returnType()) {\n                throw new IllegalStateException(mh + \": Unexpected return type \" + type.returnType());\n            }\n        }\n        List<Class<?>> wantedTypes = new ArrayList<>(wantedType.parameterList());\n        Class<?>[] types = type.parameterArray();\n        for (int i = 1; i < types.length; i++) {\n            int pos = wantedTypes.indexOf(types[i]);\n            if (pos < 0) {\n                dbFetchers.put(types[i], databases.queryDatabase(types[i]));\n                wantedTypes.add(types[i]);\n            }\n        }\n        if (wantedType.parameterCount() < wantedTypes.size())\n            wantedType = MethodType.methodType(wantedType.returnType(), wantedTypes);\n        int[] map = new int[wantedTypes.size()];\n        Arrays.fill(map, -1);\n        map[0] = 0;\n        wantedTypes.set(0, null); // self-reference\n        for (int i = 1; i < types.length; i++) {\n            int pos = wantedTypes.indexOf(types[i]);\n            if (pos < 0) {\n                throw new IllegalStateException(mh + \": Duplicate parameter type \" + types[i]);\n            }\n            wantedTypes.set(pos, null);\n            map[i] = pos;\n        }\n        Class<?>[] missingClasses = wantedTypes.stream().filter(Objects::nonNull).toArray(Class<?>[]::new);\n        if (missingClasses.length > 0) {\n            int pos = 0;\n            for (int i = types.length; i < map.length; i++) {\n                while (wantedTypes.get(pos) == null)\n                    pos++;\n                map[i] = pos++;\n            }\n            result = MethodHandles.dropArguments(result, types.length, missingClasses);\n        }\n        result = MethodHandles.permuteArguments(result, wantedType, map);\n        return result;\n    }\n\n    public WarningType getWarningType(String typeName) {\n        return wts.get(typeName);\n    }\n\n    @Override\n    public String toString() {\n        return clazz.getName().replace(DetectorRegistry.DETECTORS_PACKAGE, \"internal\");\n    }\n\n    public Object newInstance() {\n        try {\n            return clazz.newInstance();\n        } catch (InstantiationException | IllegalAccessException e) {\n            throw new InternalError(e);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/DetectorRegistry.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport com.strobel.assembler.ir.Instruction;\nimport com.strobel.assembler.ir.OpCode;\nimport com.strobel.assembler.metadata.MetadataSystem;\nimport com.strobel.assembler.metadata.MethodBody;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.DecompilerContext;\nimport com.strobel.decompiler.ast.AstBuilder;\nimport com.strobel.decompiler.ast.AstOptimizationStep;\nimport com.strobel.decompiler.ast.AstOptimizer;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Lambda;\nimport com.strobel.decompiler.ast.Node;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.ErrorMessage;\nimport one.util.huntbugs.db.FieldStats;\nimport one.util.huntbugs.db.MethodStats;\nimport one.util.huntbugs.flow.CFG;\nimport one.util.huntbugs.flow.ClassFields;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.repo.Repository;\nimport one.util.huntbugs.repo.RepositoryVisitor;\nimport one.util.huntbugs.spi.HuntBugsPlugin;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.Messages.Message;\nimport one.util.huntbugs.warning.Role.NumberRole;\nimport one.util.huntbugs.warning.WarningType;\n\nimport java.io.PrintStream;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Deque;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.ServiceLoader;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class DetectorRegistry {\n    private static final WarningType METHOD_TOO_LARGE = new WarningType(\"System\", \"MethodTooLarge\", 30);\n    private static final NumberRole BYTECODE_SIZE = NumberRole.forName(\"BYTECODE_SIZE\");\n    private static final NumberRole LIMIT = NumberRole.forName(\"LIMIT\");\n\n    static final String DETECTORS_PACKAGE = \"one.util.huntbugs.detect\";\n\n    private final Map<WarningType, Detector> typeToDetector = new HashMap<>();\n    private final List<Detector> detectors = new ArrayList<>();\n    private final Context ctx;\n    private final Detector systemDetector;\n\n    private final DatabaseRegistry databases;\n    private final Function<TypeReference, FieldStats> fieldStatsDb;\n    private final Function<TypeReference, MethodStats> methodStatsDb;\n\n    public static class SystemDetector {\n    }\n\n    public DetectorRegistry(Context ctx) {\n        this.ctx = ctx;\n        this.databases = new DatabaseRegistry(ctx);\n        ctx.incStat(\"WarningTypes.Total\");\n        Map<String, WarningType> systemWarnings = createWarningMap(Stream.of(METHOD_TOO_LARGE));\n        try {\n            this.systemDetector = createDetector(SystemDetector.class, systemWarnings);\n        } catch (IllegalAccessException e) {\n            throw new InternalError(e);\n        }\n        this.fieldStatsDb = databases.queryDatabase(FieldStats.class);\n        this.methodStatsDb = databases.queryDatabase(MethodStats.class);\n        init();\n    }\n\n    private Map<String, WarningType> createWarningMap(Stream<WarningType> stream) {\n        return stream.map(ctx.getOptions().getRule()::adjust).collect(\n            Collectors.toMap(WarningType::getName, Function.identity()));\n    }\n\n    private List<WarningDefinition> getDefinitions(Class<?> clazz) {\n        WarningDefinition[] wds = clazz.getAnnotationsByType(WarningDefinition.class);\n        if (wds == null)\n            return Collections.emptyList();\n        return Arrays.asList(wds);\n    }\n\n    boolean addDetector(Class<?> clazz) {\n        List<WarningDefinition> wds = getDefinitions(clazz);\n        if (wds.isEmpty())\n            return false;\n        try {\n            wds.forEach(wd -> ctx.incStat(\"WarningTypes.Total\"));\n            Map<String, WarningType> wts = createWarningMap(wds.stream().map(WarningType::new));\n            Detector detector = createDetector(clazz, wts);\n            if (detector == null)\n                return false;\n            detectors.add(detector);\n        } catch (Exception e) {\n            ctx.addError(new ErrorMessage(clazz.getName(), null, null, null, -1, e));\n        }\n        return true;\n    }\n\n    private Detector createDetector(Class<?> clazz, Map<String, WarningType> wts) throws IllegalAccessException {\n        List<WarningType> activeWts = wts.values().stream().filter(wt -> wt.getMaxScore() >= ctx.getOptions().minScore)\n                .collect(Collectors.toList());\n        if (activeWts.isEmpty())\n            return null;\n        Detector detector = new Detector(wts, clazz, databases);\n        activeWts.forEach(wt -> {\n            typeToDetector.put(wt, detector);\n            ctx.incStat(\"WarningTypes\");\n        });\n        return detector;\n    }\n\n    void init() {\n\n        // adding HuntBugs built-in detectors\n        Repository selfRepo = Repository.createSelfRepository();\n        String pkg = DETECTORS_PACKAGE.replace('.', '/');\n        selfRepo.visit(pkg, new DetectorVisitor(pkg, false));\n\n        // adding HuntBugs 3-rd party detectors if any\n        for (HuntBugsPlugin huntBugsPlugin : ServiceLoader.load(HuntBugsPlugin.class)) {\n            Repository pluginRepository = Repository.createPluginRepository(huntBugsPlugin);\n            String pluginDetectorPackage = huntBugsPlugin.detectorPackage().replace('.', '/');\n            pluginRepository.visit(pluginDetectorPackage, new DetectorVisitor(pluginDetectorPackage, true));\n        }\n\n    }\n\n    private void visitChildren(Node node, NodeChain parents, List<MethodContext> list, MethodData mdata) {\n        if (node instanceof Lambda) {\n            MethodDefinition curMethod = mdata.realMethod;\n            CFG curCFG = mdata.cfg;\n            mdata.realMethod = Nodes.getLambdaMethod((Lambda) node);\n            mdata.cfg = curCFG == null ? null : curCFG.getLambdaCFG((Lambda) node);\n            Iterable<Node> children = Nodes.getChildren(node);\n            NodeChain newChain = new NodeChain(parents, node);\n            for (Node child : children)\n                visitChildren(child, newChain, list, mdata);\n            mdata.realMethod = curMethod;\n            mdata.cfg = curCFG;\n        } else {\n            Iterable<Node> children = Nodes.getChildren(node);\n            NodeChain newChain = new NodeChain(parents, node);\n            for (Node child : children)\n                visitChildren(child, newChain, list, mdata);\n        }\n        mdata.parents = parents;\n        for (MethodContext mc : list) {\n            mc.visitNode(node);\n        }\n    }\n\n    public boolean hasDatabases() {\n        return !databases.instances.isEmpty();\n    }\n\n    public void populateDatabases(TypeDefinition type) {\n        databases.visitType(type);\n        for (TypeDefinition subType : type.getDeclaredTypes()) {\n            populateDatabases(subType);\n        }\n    }\n\n    public void analyzeClass(TypeDefinition type) {\n        ctx.incStat(\"TotalClasses\");\n        \n        ClassData cdata = new ClassData(type);\n        ClassFields cf = new ClassFields(type, fieldStatsDb.apply(type), methodStatsDb.apply(type));\n        \n        List<MethodDefinition> declMethods = new ArrayList<>(type.getDeclaredMethods());\n        sortMethods(declMethods);\n        List<FieldData> fields = type.getDeclaredFields().stream().map(FieldData::new).collect(Collectors.toList());\n\n        type.getDeclaredMethods().forEach(cdata::registerAsserter);\n        type.getDeclaredFields().forEach(cdata::registerAsserter);\n\n        ClassContext[] ccs = detectors.stream().map(d -> new ClassContext(ctx, cdata, d)).filter(\n            ClassContext::visitClass).toArray(ClassContext[]::new);\n        \n        for (MethodDefinition md : declMethods) {\n            if(!md.isSpecialName()) {\n                cf.clearCtorData();\n            }\n            if(md.isSynthetic() && md.getName().startsWith(\"lambda$\"))\n                continue;\n            MethodData mdata = new MethodData(md);\n\n            Map<Boolean, List<MethodContext>> mcs = Stream.of(ccs).map(cc -> cc.forMethod(mdata)).collect(\n                Collectors.partitioningBy(MethodContext::visitMethod));\n\n            MethodBody body = md.getBody();\n            if (body != null) {\n                if (body.getCodeSize() > ctx.getOptions().maxMethodSize) {\n                    if (systemDetector != null) {\n                        MethodContext mc = new ClassContext(ctx, cdata, systemDetector).forMethod(mdata);\n                        mc.report(METHOD_TOO_LARGE.getName(), 0, BYTECODE_SIZE.create(body.getCodeSize()), LIMIT.create(\n                            ctx.getOptions().maxMethodSize));\n                        mc.finalizeMethod();\n                    }\n                } else if (!mcs.get(true).isEmpty()) {\n                    final DecompilerContext context = new DecompilerContext();\n\n                    context.setCurrentMethod(md);\n                    context.setCurrentType(type);\n                    Block methodAst = new Block();\n                    try {\n                        methodAst.getBody().addAll(AstBuilder.build(body, true, context));\n                        AstOptimizer.optimize(context, methodAst, AstOptimizationStep.None);\n                        mdata.cfg = CFG.build(md, methodAst);\n                        mdata.origParams = ValuesFlow.annotate(ctx, md, cf, mdata.cfg);\n                        mdata.fullyAnalyzed = true;\n                    } catch (Throwable t) {\n                        ctx.addError(new ErrorMessage(null, type.getFullName(), md.getFullName(), md.getSignature(),\n                                -1, t));\n                    }\n                    visitChildren(methodAst, null, mcs.get(true), mdata);\n                }\n            } else {\n                mdata.fullyAnalyzed = true;\n            }\n            for (MethodContext mc : mcs.get(true)) {\n                mc.visitAfterMethod();\n                mc.finalizeMethod();\n            }\n            for (MethodContext mc : mcs.get(false)) {\n                mc.finalizeMethod();\n            }\n        }\n        for(FieldData fdata : fields) {\n            for(ClassContext cc : ccs) {\n                cc.forField(fdata).visitField();\n            }\n        }\n        for(ClassContext cc : ccs) {\n            cc.visitAfterClass();\n        }\n        cdata.finish(ctx);\n\n        for (TypeDefinition subType : type.getDeclaredTypes()) {\n            analyzeClass(subType);\n        }\n    }\n\n    private void sortMethods(List<MethodDefinition> declMethods) {\n        declMethods.sort(Comparator.comparingInt(md ->\n                md.isTypeInitializer() ? 0 :\n                    md.isConstructor() ? 1 : 2));\n        int start = -1, end = declMethods.size();\n        for (int i = 0; i < end; i++) {\n            if(start == -1) {\n                if(declMethods.get(i).isConstructor())\n                    start = i;\n            } else \n                if(!declMethods.get(i).isConstructor()) {\n                    end = i;\n            }\n        }\n        if(start >= 0) {\n            sortConstructors(declMethods.subList(start, end));\n        }\n    }\n\n    private void sortConstructors(List<MethodDefinition> ctors) {\n        if(ctors.size() < 2)\n            return;\n        Map<MethodDefinition, MethodDefinition> deps = new HashMap<>();\n        for(MethodDefinition ctor : ctors) {\n            MethodBody body = ctor.getBody();\n            if(body != null) {\n                for(Instruction instr : body.getInstructions()) {\n                    if(instr.getOpCode() == OpCode.INVOKESPECIAL) {\n                        MethodReference mr = instr.getOperand(0);\n                        if(mr.getDeclaringType().isEquivalentTo(ctor.getDeclaringType()) && mr.isConstructor()) {\n                            deps.put(ctor, mr.resolve());\n                        }\n                        break;\n                    }\n                }\n            }\n        }\n        Set<MethodDefinition> result = new LinkedHashSet<>();\n        for(MethodDefinition ctor : ctors) {\n            Deque<MethodDefinition> chain = new ArrayDeque<>();\n            MethodDefinition cur = ctor;\n            while(cur != null && !result.contains(cur)) {\n                chain.addFirst(cur);\n                cur = deps.get(cur);\n            }\n            result.addAll(chain);\n        }\n        if(result.size() != ctors.size())\n            throw new InternalError();\n        int i=0;\n        for(MethodDefinition ctor : result) {\n            ctors.set(i++, ctor);\n        }\n    }\n\n    private void printTree(PrintStream out, List<String> result, String arrow) {\n        result.sort(null);\n        String lastCategory = arrow;\n        for (int i = 0; i < result.size(); i++) {\n            String str = result.get(i);\n            if (str.startsWith(lastCategory)) {\n                out.printf(\"%\" + lastCategory.length() + \"s%s%n\", i == result.size() - 1\n                    || !result.get(i + 1).startsWith(lastCategory) ? \"\\\\-> \" : \"|-> \", str.substring(lastCategory\n                        .length()));\n            } else {\n                out.println(str);\n                lastCategory = str.substring(0, str.indexOf(arrow) + arrow.length());\n            }\n        }\n    }\n\n    public void reportWarningTypes(PrintStream out) {\n        List<String> result = new ArrayList<>();\n\n        String arrow = \" --> \";\n        typeToDetector.forEach((wt, detector) ->\n            result.add(wt.getCategory() + arrow + wt.getName() + arrow + detector)\n        );\n        printTree(out, result, arrow);\n        out.println(\"Total types: \" + typeToDetector.size());\n    }\n\n    public void reportDatabases(PrintStream out) {\n        List<String> result = new ArrayList<>();\n        String arrow = \" --> \";\n        detectors.forEach(det -> det.dbFetchers.keySet().forEach(db -> result.add(db.getName() + arrow + det)));\n        databases.instances.forEach((db, dbi) -> {\n            if (dbi.parentDb != null) {\n                result.add(dbi.parentDb.getClass().getName() + arrow + \"Derived DB: \" + db.getName());\n            }\n        });\n        printTree(out, result, arrow);\n        out.println(\"Total databases: \" + databases.instances.size());\n    }\n\n    public void reportTitles(PrintStream out) {\n        List<String> rows = new ArrayList<>();\n        warningTypes().forEach(wt -> {\n            Message msg = ctx.getMessages().getMessagesForType(wt);\n            ctx.incStat(\"Messages.Total\");\n            if (msg.getTitle().equals(wt.getName())) {\n                rows.add(wt.getName() + \": ?\");\n            } else {\n                ctx.incStat(\"Messages\");\n                rows.add(wt.getName() + \": \" + msg.getTitle());\n            }\n        });\n        rows.sort(null);\n        rows.forEach(out::println);\n    }\n\n    public WarningType getWarningType(String typeName) {\n        return typeToDetector.keySet().stream().filter(wt -> wt.getName().equals(typeName)).findFirst().orElse(null);\n    }\n    \n    public Stream<WarningType> warningTypes() {\n        return typeToDetector.keySet().stream();\n    }\n\n    private class DetectorVisitor implements RepositoryVisitor {\n\n        private String packageToVisit;\n\n        private boolean external;\n\n        DetectorVisitor(String packageToVisit, boolean external) {\n            this.packageToVisit = packageToVisit;\n            this.external = external;\n        }\n\n        @Override\n        public boolean visitPackage(String packageName) {\n            return packageName.equals(packageToVisit);\n        }\n\n        @Override\n        public void visitClass(String className) {\n            String name = className.replace('/', '.');\n            try {\n                ctx.incStat(\"Detectors.Total\");\n                if (addDetector(MetadataSystem.class.getClassLoader().loadClass(name))) {\n                    ctx.incStat(\"Detectors\");\n                    if (external) {\n                        ctx.incStat(\"Detectors from HuntBugs plugins\");\n                    }\n                }\n            } catch (ClassNotFoundException e) {\n                ctx.addError(new ErrorMessage(name, null, null, null, -1, e));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/ElementContext.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.warning.WarningType;\n\n/**\n * @author Tagir Valeev\n *\n */\nabstract class ElementContext {\n    protected final Context ctx;\n    protected final Detector detector;\n\n    public ElementContext(Context ctx, Detector detector) {\n        this.ctx = ctx;\n        this.detector = detector;\n    }\n\n    /**\n     * Report an internal analysis error. Alternatively detector may just throw any exception instead.\n     * \n     * @param message message to report\n     */\n    abstract public void error(String message);\n\n    protected WarningType resolveWarningType(String warning, int priority) {\n        WarningType wt = detector.getWarningType(warning);\n        if (wt == null) {\n            error(\"Tries to report a warning of non-declared type: \" + warning);\n            return null;\n        }\n        if (priority < 0) {\n            error(\"Tries to report a warning \" + warning + \" with negative priority \" + priority);\n            return null;\n        }\n        if (wt.getMaxScore() - priority < ctx.getOptions().minScore) {\n            return null;\n        }\n        return wt;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/FieldContext.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport java.lang.invoke.MethodHandle;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.ErrorMessage;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningType;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\npublic class FieldContext extends ElementContext {\n    private final ClassContext cc;\n    private final FieldData fdata;\n    private final Object det;\n\n    FieldContext(Context ctx, ClassContext cc, FieldData fdata) {\n        super(ctx, cc.detector);\n        this.cc = cc;\n        this.fdata = fdata;\n        this.det = cc.det;\n    }\n\n    void visitField() {\n        for(MethodHandle mh : detector.fieldVisitors) {\n            try {\n                detector.bindDatabases(Detector.FIELD_VISITOR_TYPE.parameterCount(), cc.type, mh)\n                        .invoke(det, this, fdata.fd, cc.type);\n            } catch (Throwable e) {\n                ctx.addError(new ErrorMessage(detector, fdata.fd, -1, e));\n            }\n        }\n    }\n\n    public void report(String warning, int priority, WarningAnnotation<?>... annotations) {\n        WarningType wt = resolveWarningType(warning, priority);\n        if(wt == null)\n            return;\n        List<WarningAnnotation<?>> anno = new ArrayList<>();\n        anno.addAll(cc.getTypeSpecificAnnotations());\n        anno.add(Roles.FIELD.create(fdata.fd));\n        anno.addAll(Arrays.asList(annotations));\n        Warning w = new Warning(wt, priority, anno);\n        if(!cc.cdata.filter.test(w))\n            return;\n        cc.getMemberAsserter(fdata.fd).checkWarning(this::error, w);\n        MemberInfo mi = w.getAnnotation(Roles.METHOD);\n        if(mi != null)\n            cc.cdata.getAsserter(mi).checkWarning(this::error, w);\n        ctx.addWarning(w);\n    }\n\n    @Override\n    public void error(String message) {\n        ctx.addError(new ErrorMessage(detector, fdata.fd, -1, message));\n    }\n\n    @Override\n    public String toString() {\n        return \"Analyzing field \" + fdata + \" with detector \" + detector;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/FieldData.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\n\n/**\n * @author Tagir Valeev\n *\n */\nfinal class FieldData {\n    final FieldDefinition fd;\n\n    public FieldData(FieldDefinition fd) {\n        this.fd = fd;\n    }\n\n    @Override\n    public String toString() {\n        return fd.getDeclaringType() + \".\" + fd.getName()+\" : \"+fd.getSignature();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/MethodContext.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport java.lang.invoke.MethodHandle;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.ErrorMessage;\nimport one.util.huntbugs.flow.CodeBlock;\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.ValuesFlow;\nimport one.util.huntbugs.flow.CFG;\nimport one.util.huntbugs.flow.CFG.EdgeType;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningAnnotation.Location;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\nimport one.util.huntbugs.warning.WarningType;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Variable;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class MethodContext extends ElementContext {\n    static class WarningInfo {\n        private final WarningType type;\n        private int priority;\n        private final List<WarningAnnotation<?>> annotations;\n        private Location bestLocation;\n        private final List<Location> locations = new ArrayList<>();\n\n        public WarningInfo(WarningType type, int priority, Location loc, List<WarningAnnotation<?>> annotations) {\n            super();\n            this.type = type;\n            this.priority = priority;\n            this.annotations = annotations;\n            this.bestLocation = loc;\n        }\n\n        boolean tryMerge(WarningInfo other) {\n            if (!other.type.equals(type) || !other.annotations.equals(annotations)) {\n                return false;\n            }\n            if (other.priority < priority) {\n                this.priority = other.priority;\n                if (bestLocation != null)\n                    this.locations.add(bestLocation);\n                bestLocation = other.bestLocation;\n            } else {\n                if (other.bestLocation != null) {\n                    this.locations.add(other.bestLocation);\n                }\n            }\n            this.locations.addAll(other.locations);\n            return true;\n        }\n\n        Warning build() {\n            if (bestLocation != null)\n                annotations.add(Roles.LOCATION.create(bestLocation));\n            locations.stream().map(Roles.ANOTHER_INSTANCE::create).forEach(annotations::add);\n            return new Warning(type, priority, annotations);\n        }\n    }\n\n    private final MethodData mdata;\n    private final Object det;\n    private final ClassContext cc;\n    private WarningInfo lastWarning;\n    private final List<MethodHandle> astVisitors;\n\n    MethodContext(Context ctx, ClassContext сс, MethodData md) {\n        super(ctx, сс.detector);\n        this.cc = сс;\n        this.mdata = md;\n        this.det = сс.det;\n        astVisitors = detector.astVisitors.stream().filter(vi -> vi.isApplicable(md.mainMethod)).map(\n            vi -> vi.bind(сс.type)).collect(Collectors.toCollection(ArrayList::new));\n    }\n\n    boolean visitMethod() {\n        for(MethodHandle mh : detector.methodVisitors) {\n            try {\n                if (!(boolean) detector.bindDatabases(Detector.METHOD_VISITOR_TYPE.parameterCount(), cc.type, mh)\n                        .invoke(det, this, mdata.mainMethod, cc.type)) {\n                    return false;\n                }\n            } catch (Throwable e) {\n                ctx.addError(new ErrorMessage(detector, mdata.mainMethod, -1, e));\n            }\n        }\n        return !astVisitors.isEmpty() || !detector.methodAfterVisitors.isEmpty();\n    }\n\n    void visitAfterMethod() {\n        for(MethodHandle mh : detector.methodVisitors) {\n            try {\n                detector.bindDatabases(Detector.METHOD_VISITOR_TYPE.parameterCount(), cc.type, mh)\n                        .invoke(det, this, mdata.mainMethod, cc.type);\n            } catch (Throwable e) {\n                ctx.addError(new ErrorMessage(detector, mdata.mainMethod, -1, e));\n            }\n        }\n    }\n\n    boolean visitNode(Node node) {\n        for (Iterator<MethodHandle> it = astVisitors.iterator(); it.hasNext();) {\n            try {\n                MethodHandle mh = it.next();\n                if (!(boolean) mh.invoke(det, node, mdata.parents, this, mdata.mainMethod, cc.type)) {\n                    it.remove();\n                }\n            } catch (Throwable e) {\n                ctx.addError(new ErrorMessage(detector, mdata.mainMethod, -1, e));\n            }\n        }\n        return !astVisitors.isEmpty();\n    }\n\n    void finalizeMethod() {\n        if (lastWarning != null) {\n            reportWarning(lastWarning.build());\n        }\n    }\n\n    public void report(String warning, int priority, WarningAnnotation<?>... annotations) {\n        report(warning, priority, null, annotations);\n    }\n\n    public void report(String warning, int priority, Node node, Collection<WarningAnnotation<?>> annotations) {\n        WarningType wt = resolveWarningType(warning, priority);\n        if(wt == null)\n            return;\n        List<WarningAnnotation<?>> anno = new ArrayList<>();\n        anno.addAll(cc.getTypeSpecificAnnotations());\n        anno.addAll(mdata.getMethodSpecificAnnotations());\n        Location loc = getLocation(node);\n        if (node instanceof Expression) {\n            Expression expr = (Expression) node;\n            Object operand = expr.getOperand();\n            if (operand instanceof Variable) {\n                anno.add(WarningAnnotation.forVariable((Variable) operand));\n                operand = ValuesFlow.getSource(expr).getOperand();\n            }\n            if (operand instanceof FieldReference && !annotations.stream().anyMatch(wa -> wa\n                .getRole() == Roles.FIELD)) {\n                anno.add(Roles.FIELD.create((FieldReference) operand));\n            }\n            if (operand instanceof MethodReference && !annotations.stream().anyMatch(wa -> wa\n                    .getRole() == Roles.CALLED_METHOD)) {\n                MethodReference mr = (MethodReference) operand;\n                anno.add(Roles.CALLED_METHOD.create(mr));\n            }\n        }\n        anno.addAll(annotations);\n        WarningInfo info = new WarningInfo(wt, priority, loc, anno);\n        if (lastWarning == null) {\n            lastWarning = info;\n        } else if (!lastWarning.tryMerge(info)) {\n            reportWarning(lastWarning.build());\n            lastWarning = info;\n        }\n    }\n\n    public void report(String warning, int priority, Node node, WarningAnnotation<?>... annotations) {\n        report(warning, priority, node, Arrays.asList(annotations));\n    }\n\n    private void reportWarning(Warning warn) {\n        if(!cc.cdata.filter.test(warn))\n            return;\n        if(cc.cdata.hasAsserters()) {\n            MemberInfo field = warn.getAnnotation(Roles.FIELD);\n            if(field != null)\n                cc.cdata.getAsserter(field).checkWarning(this::error, warn);\n            cc.getMemberAsserter(mdata.mainMethod).checkWarning(this::error, warn);\n        }\n        ctx.addWarning(warn);\n    }\n\n    /**\n     * @param node to get the location for\n     * @return location object which describes given node\n     */\n    public Location getLocation(Node node) {\n        return mdata.getLocation(node);\n    }\n\n    /**\n     * Forget last bug reported by current detector. Subsequent calls of this\n     * method have no effect if no new bugs were reported.\n     */\n    public void forgetLastBug() {\n        lastWarning = null;\n    }\n\n    @Override\n    public void error(String message) {\n        ctx.addError(new ErrorMessage(detector, mdata.mainMethod, -1, message));\n    }\n\n    /**\n     * @return true if the method is fully annotated via Inf.SOURCE\n     */\n    public boolean isAnnotated() {\n        return mdata.origParams != null;\n    }\n    \n    public CFG getCFG() {\n        return mdata.cfg;\n    }\n    \n    /**\n     * @param expr expression to test\n     * @return true if given expression is reachable\n     */\n    public boolean isReachable(Expression expr) {\n        return mdata.cfg == null || mdata.cfg.isReachable(expr);\n    }\n    \n    public boolean isAlwaysReachable(Expression from, Expression to) {\n        return mdata.cfg != null && mdata.cfg.isAlwaysReachable(from, to);\n    }\n\n    public boolean mayTerminateImplicitly(Expression expr) {\n        return mdata.cfg != null && mdata.cfg.mayTerminateImplicitly(expr);\n    }\n    \n    public CodeBlock findDeadCode(Expression expr, EdgeType edgeType) {\n        if(mdata.cfg == null)\n            return null;\n        return mdata.cfg.findDeadCode(expr, edgeType);\n    }\n    \n    \n    public Set<Expression> getParameterUsages(ParameterDefinition pd) {\n        if(mdata.origParams == null)\n            return null;\n        for(Expression expr : mdata.origParams) {\n            if(expr.getOperand() == pd)\n                return Inf.BACKLINK.findUsages(expr);\n        }\n        return null;\n    }\n\n    @Override\n    public String toString() {\n        return \"Analyzing method \" + mdata + \" with detector \" + detector;\n    }\n\n    public boolean isFullyAnalyzed() {\n        return mdata.fullyAnalyzed;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/MethodData.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport one.util.huntbugs.flow.CFG;\nimport one.util.huntbugs.util.NodeChain;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.WarningAnnotation;\nimport one.util.huntbugs.warning.WarningAnnotation.Location;\n\nimport com.strobel.assembler.ir.attributes.LineNumberTableAttribute;\nimport com.strobel.assembler.ir.attributes.SourceAttribute;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Lambda;\nimport com.strobel.decompiler.ast.Loop;\nimport com.strobel.decompiler.ast.LoopType;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Switch;\nimport com.strobel.decompiler.languages.java.LineNumberTableConverter;\nimport com.strobel.decompiler.languages.java.OffsetToLineNumberConverter;\n\n/**\n * @author Tagir Valeev\n *\n */\nfinal class MethodData {\n    final MethodDefinition mainMethod;\n    List<WarningAnnotation<?>> annot;\n    // May differ from mainMethod when inside lambda\n    MethodDefinition realMethod;\n    NodeChain parents;\n\n    private OffsetToLineNumberConverter ltc;\n    List<Expression> origParams;\n    boolean fullyAnalyzed;\n    CFG cfg;\n\n    MethodData(MethodDefinition md) {\n        this.mainMethod = this.realMethod = md;\n    }\n\n    int getLineNumber(int offset) {\n        int line = getConverter().getLineForOffset(offset);\n        return line == OffsetToLineNumberConverter.UNKNOWN_LINE_NUMBER ? -1 : line;\n    }\n\n    List<WarningAnnotation<?>> getMethodSpecificAnnotations() {\n        if (annot == null) {\n            annot = Collections.singletonList(Roles.METHOD.create(mainMethod));\n        }\n        return annot;\n    }\n\n    private OffsetToLineNumberConverter getConverter() {\n        if (realMethod != mainMethod)\n            return createConverter(realMethod);\n        if (ltc == null)\n            ltc = createConverter(mainMethod);\n        return ltc;\n    }\n\n    private static OffsetToLineNumberConverter createConverter(MethodDefinition md) {\n        for (SourceAttribute sa : md.getSourceAttributes()) {\n            if (sa instanceof LineNumberTableAttribute) {\n                return new LineNumberTableConverter((LineNumberTableAttribute) sa);\n            }\n        }\n        return OffsetToLineNumberConverter.NOOP_CONVERTER;\n    }\n    \n    private static int getOffset(Node node) {\n        if (node instanceof Expression) {\n            Expression expr = (Expression) node;\n            return expr.getOffset();\n        } else if (node instanceof Condition) {\n            return ((Condition) node).getCondition().getOffset();\n        } else if (node instanceof Block) {\n            List<Node> body = ((Block) node).getBody();\n            return body.stream().mapToInt(MethodData::getOffset).filter(off -> off != Expression.MYSTERY_OFFSET).findFirst().orElse(\n                Expression.MYSTERY_OFFSET);\n        } else if (node instanceof Loop) {\n            Loop loop = (Loop)node;\n            return loop.getLoopType() == LoopType.PreCondition && loop.getCondition() != null ? loop.getCondition()\n                    .getOffset() : getOffset(loop.getBody());\n        } else if (node instanceof Switch) {\n            return ((Switch)node).getCondition().getOffset();\n        }\n        return Expression.MYSTERY_OFFSET;\n    }\n\n    Location getLocation(Node node) {\n        NodeChain nc = parents;\n        while(true) {\n            int offset = getOffset(node);\n            if(offset != Expression.MYSTERY_OFFSET)\n                return new Location(offset, getLineNumber(offset));\n            if(nc == null || nc.getNode() instanceof Lambda)\n                return new Location(0, getLineNumber(0));\n            // TODO: better support of catch blocks\n            while(true) {\n                Node parentNode = nc.getNode();\n                nc = nc.getParent();\n                List<Node> children = parentNode.getChildren();\n                int idx = children.indexOf(node);\n                if(idx < children.size() - 1) {\n                    node = children.get(idx+1);\n                    break;\n                }\n                node = parentNode;\n                if(nc == null || nc.getNode() instanceof Lambda) {\n                    offset = Math.max(realMethod.getBody().getCodeSize()-1, 0);\n                    return new Location(offset, getLineNumber(offset));\n                }\n            }\n        }\n    }\n\n    @Override\n    public String toString() {\n        return mainMethod.getDeclaringType() + \".\" + mainMethod;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/AssertNoWarning.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author Tagir Valeev\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR})\npublic @interface AssertNoWarning {\n    String value();\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/AssertWarning.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport one.util.huntbugs.warning.Warning;\n\n/**\n * @author Tagir Valeev\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR})\npublic @interface AssertWarning {\n    String value();\n\n    int minScore() default Warning.MIN_SCORE;\n\n    int maxScore() default Warning.MAX_SCORE;\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/AstNodes.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport one.util.huntbugs.util.NodeChain;\n\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\n/**\n * Type of the nodes visited by {@link AstVisitor}\n */\npublic enum AstNodes {\n    /**\n     * Visits all method nodes. Additional allowed argument types: {@link Node}, {@link NodeChain}.\n     * May return void or boolean: if false then the rest of the method will be skipped.\n     */\n    ALL,\n    /**\n     * Visits only expressions. Additional allowed argument types: {@link Expression}, {@link NodeChain}.\n     * May return void or boolean: if false then the rest of the method will be skipped.\n     */\n    EXPRESSIONS,\n    /**\n     * Visits only method root node. Additional allowed argument type: {@link Block}.\n     * Returns void.\n     */\n    ROOT\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/AstVisitor.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport one.util.huntbugs.registry.MethodContext;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\n\n/**\n * Method in detector class which called for AST nodes.\n * \n * <p>\n * Allowed parameter types (no repeats): {@link MethodContext},\n * {@link MethodDefinition}, {@link TypeDefinition} or any registered databases\n * (see {@link TypeDatabase}, {@link TypeDatabaseItem})\n * \n * <p>\n * For additional allowed types and allowed return values see the {@link AstNodes} description.\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface AstVisitor {\n    AstNodes nodes() default AstNodes.ALL;\n    \n    String methodName() default \"\";\n\n    String methodSignature() default \"\";\n    \n    int minVersion() default 0;\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/ClassVisitor.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport one.util.huntbugs.registry.ClassContext;\n\nimport com.strobel.assembler.metadata.TypeDefinition;\n\n/**\n * Method in detector class which called for every visited class.\n * \n * <p>\n * Allowed parameter types (no repeats): {@link ClassContext},\n * {@link TypeDefinition} or any registered databases (see {@link TypeDatabase},\n * {@link TypeDatabaseItem})\n * \n * <p>\n * May return boolean or void. If returns false, any other visitors (e.g.\n * {@link MethodVisitor}, {@link AstVisitor}) defined in this detector will be\n * skipped for this class.\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface ClassVisitor {\n    VisitOrder order() default VisitOrder.BEFORE;\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/FieldVisitor.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport one.util.huntbugs.registry.FieldContext;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\n\n/**\n * Method in detector class which called for every method in the class.\n * \n * <p>\n * Allowed parameter types (no repeats): {@link FieldContext},\n * {@link FieldDefinition}, {@link TypeDefinition} or any registered databases\n * (see {@link TypeDatabase}, {@link TypeDatabaseItem})\n * \n * <p>\n * Must return void.\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface FieldVisitor {\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/MethodVisitor.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport one.util.huntbugs.registry.MethodContext;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\n\n/**\n * Method in detector class which called for every method in the class.\n * \n * <p>\n * Allowed parameter types (no repeats): {@link MethodContext},\n * {@link MethodDefinition}, {@link TypeDefinition} or any registered databases\n * (see {@link TypeDatabase}, {@link TypeDatabaseItem})\n * \n * <p>\n * May return boolean or void. If returns false, any other visitors (e.g.\n * {@link AstVisitor}) defined in this detector will be skipped.\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface MethodVisitor {\n    VisitOrder order() default VisitOrder.BEFORE;\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/TypeDatabase.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author Tagir Valeev\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface TypeDatabase {\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/TypeDatabaseItem.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport one.util.huntbugs.registry.AbstractTypeDatabase;\n\n/**\n * @author Tagir Valeev\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface TypeDatabaseItem {\n    Class<? extends AbstractTypeDatabase<?>> parentDatabase();\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/VisitOrder.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\n/**\n * Specifies when visitor should be called\n */\npublic enum VisitOrder {\n    /**\n     * Call this visitor before nested objects are visited\n     */\n    BEFORE,\n    /**\n     * Call this visitor after nested objects are visited\n     */\n    AFTER\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/WarningDefinition.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Repeatable(WarningDefinitions.class)\npublic @interface WarningDefinition {\n    String category();\n    \n    String name();\n    \n    int maxScore();\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/registry/anno/WarningDefinitions.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry.anno;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE})\npublic @interface WarningDefinitions {\n    WarningDefinition[] value();\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/repo/AuxRepository.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.repo;\n\nimport com.strobel.assembler.metadata.ITypeLoader;\n\n/**\n * Repository for auxiliary classes which should not be visited normally but should be accessible\n * \n * @author Tagir Valeev\n *\n */\npublic class AuxRepository implements Repository {\n    private final ITypeLoader loader;\n\n    public AuxRepository(ITypeLoader loader) {\n        this.loader = loader;\n    }\n\n    @Override\n    public ITypeLoader createTypeLoader() {\n        return loader;\n    }\n\n    @Override\n    public void visit(String rootPackage, RepositoryVisitor visitor) {\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/repo/CompositeRepository.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.repo;\n\nimport java.util.List;\nimport java.util.Objects;\nimport com.strobel.assembler.metadata.CompositeTypeLoader;\nimport com.strobel.assembler.metadata.ITypeLoader;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class CompositeRepository implements Repository {\n    private final List<Repository> repos;\n\n    public CompositeRepository(List<Repository> repos) {\n        this.repos = Objects.requireNonNull(repos);\n    }\n\n    @Override\n    public ITypeLoader createTypeLoader() {\n        return new CompositeTypeLoader(repos.stream().map(Repository::createTypeLoader).toArray(ITypeLoader[]::new));\n    }\n\n    @Override\n    public void visit(String rootPackage, RepositoryVisitor visitor) {\n        for(Repository repo : repos)\n            repo.visit(rootPackage, visitor);\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/repo/DirRepository.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.repo;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.FileVisitor;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.attribute.BasicFileAttributes;\n\nimport com.strobel.assembler.metadata.ClasspathTypeLoader;\nimport com.strobel.assembler.metadata.ITypeLoader;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class DirRepository implements Repository {\n    private final Path root;\n\n    public DirRepository(Path root) {\n        this.root = root;\n    }\n\n    @Override\n    public ITypeLoader createTypeLoader() {\n        return new ClasspathTypeLoader(root.toString());\n    }\n\n    @Override\n    public void visit(String rootPackage, RepositoryVisitor visitor) {\n        Path path = root.resolve(rootPackage);\n        if(!Files.isDirectory(path))\n            return;\n        try {\n            Files.walkFileTree(path, new FileVisitor<Path>() {\n                @Override\n                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {\n                    String pkg = root.relativize(dir).toString().replace('\\\\', '/');\n                    if(visitor.visitPackage(pkg))\n                        return FileVisitResult.CONTINUE;\n                    return FileVisitResult.SKIP_SUBTREE;\n                }\n\n                @Override\n                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {\n                    if(file.getFileName().toString().endsWith(\".class\")) {\n                        String baseName = root.relativize(file).toString();\n                        if(!baseName.contains(\"$\")) {\n                            baseName = baseName.substring(0, baseName.length() - \".class\".length()).replace('\\\\', '/');\n                            visitor.visitClass(baseName);\n                        }\n                    }\n                    return FileVisitResult.CONTINUE;\n                }\n\n                @Override\n                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {\n                    return FileVisitResult.CONTINUE;\n                }\n\n                @Override\n                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {\n                    return FileVisitResult.CONTINUE;\n                }\n            });\n        } catch (IOException e) {\n            throw new UncheckedIOException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/repo/FilteredRepository.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.repo;\n\nimport java.util.function.Predicate;\n\nimport com.strobel.assembler.metadata.ITypeLoader;\n\n/**\n * Repository which filters visited classes with given predicate\n * (though all classes still could be loaded).\n * \n * @author Tagir Valeev\n */\npublic class FilteredRepository implements Repository {\n    private final Predicate<String> classFilter;\n    private final Repository repository;\n\n    /**\n     * @param repository parent repository\n     * @param classFilter predicate which accepts internal class name (like \"a/b/c/d\") and returns true if it should be visited\n     */\n    public FilteredRepository(Repository repository, Predicate<String> classFilter) {\n        this.repository = repository;\n        this.classFilter = classFilter;\n    }\n\n    @Override\n    public ITypeLoader createTypeLoader() {\n        return repository.createTypeLoader();\n    }\n\n    @Override\n    public void visit(String rootPackage, RepositoryVisitor visitor) {\n        repository.visit(rootPackage, new RepositoryVisitor() {\n            \n            @Override\n            public boolean visitPackage(String packageName) {\n                return visitor.visitPackage(packageName);\n            }\n            \n            @Override\n            public void visitClass(String className) {\n                if(classFilter.test(className))\n                    visitor.visitClass(className);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/repo/JarRepository.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.repo;\n\nimport java.util.Enumeration;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\nimport com.strobel.assembler.metadata.ITypeLoader;\nimport com.strobel.assembler.metadata.JarTypeLoader;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class JarRepository implements Repository {\n    private final JarFile file;\n\n    public JarRepository(JarFile file) {\n        this.file = file;\n    }\n\n    @Override\n    public ITypeLoader createTypeLoader() {\n        return new JarTypeLoader(file);\n    }\n\n    @Override\n    public void visit(String rootPackage, RepositoryVisitor visitor) {\n        Enumeration<JarEntry> entries = file.entries();\n        String skipPrefix = null;\n        while(entries.hasMoreElements()) {\n            JarEntry entry = entries.nextElement();\n            if(!rootPackage.isEmpty() && !entry.getName().startsWith(rootPackage+\"/\") && !entry.getName().equals(rootPackage))\n                continue;\n            if(skipPrefix != null) {\n                if(entry.getName().startsWith(skipPrefix))\n                    continue;\n                skipPrefix = null;\n            }\n            if(entry.isDirectory()) {\n                if(!visitor.visitPackage(entry.getName())) {\n                    skipPrefix = entry.getName()+\"/\";\n                }\n            } else {\n                if(entry.getName().endsWith(\".class\")) {\n                    String className = entry.getName();\n                    className = className.substring(0, className.length()-\".class\".length());\n                    if(!className.contains(\"$\"))\n                        visitor.visitClass(className);\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/repo/Repository.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.repo;\n\nimport com.strobel.assembler.metadata.ITypeLoader;\nimport one.util.huntbugs.spi.HuntBugsPlugin;\n\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.nio.file.FileSystemNotFoundException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.security.CodeSource;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.jar.JarFile;\n\nimport static java.lang.String.format;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic interface Repository {\n    ITypeLoader createTypeLoader();\n\n    void visit(String rootPackage, RepositoryVisitor visitor);\n\n    static Repository createSelfRepository() {\n        List<Repository> repos = new ArrayList<>();\n        Set<Path> paths = new HashSet<>();\n        try {\n            Enumeration<URL> resources = CompositeRepository.class.getClassLoader().getResources(\".\");\n            while (resources.hasMoreElements()) {\n                try {\n                    Path path = Paths.get(resources.nextElement().toURI());\n                    if(paths.add(path)) {\n                        repos.add(new DirRepository(path));\n                    }\n                } catch (URISyntaxException e) {\n                    // ignore\n                }\n            }\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n\n        repos.add(createDetectorsRepo(CompositeRepository.class, \"HuntBugs Detectors\", paths));\n\n        return new CompositeRepository(repos);\n    }\n\n    static Repository createPluginRepository(HuntBugsPlugin huntBugsPlugin) {\n        Class<?> pluginClass = huntBugsPlugin.getClass();\n        String pluginName = huntBugsPlugin.name();\n        return createDetectorsRepo(pluginClass, pluginName, new HashSet<>());\n    }\n\n    static Repository createDetectorsRepo(Class<?> clazz, String pluginName, Set<Path> paths) {\n        CodeSource codeSource = clazz.getProtectionDomain().getCodeSource();\n        if (codeSource == null) {\n            throw new RuntimeException(format(\"Initializing plugin '%s' could not get code source for class %s\", pluginName, clazz.getName()));\n        }\n\n        URL url = codeSource.getLocation();\n        try {\n            Path path = Paths.get(url.toURI());\n            if(paths.add(path)) {\n                if(Files.isDirectory(path)) {\n                    return new DirRepository(path);\n                } else {\n                    return new JarRepository(new JarFile(path.toFile()));\n                }\n            } else {\n                return createNullRepository();\n            }\n        } catch (URISyntaxException | FileSystemNotFoundException | IllegalArgumentException\n                | IOException | UnsupportedOperationException e) {\n            String errorMessage = format(\"Error creating detector repository for plugin '%s'\", pluginName);\n            throw new RuntimeException(errorMessage, e);\n        }\n    }\n\n    static Repository createNullRepository() {\n        return new Repository() {\n            @Override\n            public void visit(String rootPackage, RepositoryVisitor visitor) {\n                // nothing to do\n            }\n\n            @Override\n            public ITypeLoader createTypeLoader() {\n                return (internalName, buffer) -> false;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/repo/RepositoryVisitor.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.repo;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic interface RepositoryVisitor {\n    /**\n     * @param packageName\n     * @return true if this package and its subpackages must be visited\n     */\n    public boolean visitPackage(String packageName);\n    \n    public void visitClass(String className);\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/spi/DataTests.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.spi;\n\nimport one.util.huntbugs.analysis.AnalysisOptions;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.ErrorMessage;\nimport one.util.huntbugs.analysis.HuntBugsResult;\nimport one.util.huntbugs.input.XmlReportReader;\nimport one.util.huntbugs.output.Reports;\nimport one.util.huntbugs.repo.CompositeRepository;\nimport one.util.huntbugs.repo.Repository;\n\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.ServiceLoader;\nimport java.util.stream.Collectors;\n\nimport static java.lang.String.format;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic abstract class DataTests {\n\n    public static void test(String packageToAnalyze) throws Exception {\n\n        // creating built-in and plugins repositories\n        List<Repository> repositories = new ArrayList<>();\n        repositories.add(Repository.createSelfRepository());\n        for (HuntBugsPlugin huntBugsPlugin : ServiceLoader.load(HuntBugsPlugin.class)) {\n            repositories.add(Repository.createPluginRepository(huntBugsPlugin));\n        }\n        CompositeRepository repository = new CompositeRepository(repositories);\n\n        Context ctx = new Context(repository, new AnalysisOptions());\n        ctx.analyzePackage(packageToAnalyze);\n        ctx.reportStats(System.out);\n        ctx.reportErrors(System.err);\n        ctx.reportWarnings(new PrintStream(\"target/testWarnings.out\"));\n        Path xmlReport = Paths.get(\"target/testWarnings.xml\");\n        Reports.write(xmlReport, Paths.get(\"target/testWarnings.html\"), ctx);\n        System.out.println(\"Analyzed \" + ctx.getClassesCount() + \" classes\");\n        if (ctx.getErrorCount() > 0) {\n            List<ErrorMessage> errorMessages = ctx.errors().collect(Collectors.toList());\n            throw new AssertionError(format(\"Analysis finished with %s errors: %s\", ctx.getErrorCount(), errorMessages));\n        }\n        HuntBugsResult result = XmlReportReader.read(ctx, xmlReport);\n        Path rereadReport = Paths.get(\"target/testWarnings_reread.xml\");\n        Reports.write(rereadReport, null, result);\n        byte[] expectedReport = Files.readAllBytes(xmlReport);\n        byte[] actualReport = Files.readAllBytes(rereadReport);\n        if (!Arrays.equals(expectedReport, actualReport)) {\n            String errorMessage = format(\"Expected: \\n%s\\n\\nActual: \\n%s\\n\\n\", new String(expectedReport), new String(actualReport));\n            throw new AssertionError(errorMessage);\n        }\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/spi/HuntBugsPlugin.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.spi;\n\n/**\n * This is extension point for 3-rd party detector providers.\n *\n * @author Mihails Volkovs\n *\n */\npublic interface HuntBugsPlugin {\n\n    String name();\n\n    String detectorPackage();\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/AccessLevel.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport com.strobel.assembler.metadata.FieldDefinition;\nimport com.strobel.assembler.metadata.Flags;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\n\n/**\n * @author shustkost\n *\n */\npublic enum AccessLevel {\n    PUBLIC, PROTECTED, PACKAGE, PRIVATE;\n\n    public AccessLevel min(AccessLevel other) {\n        return other.ordinal() > ordinal() ? other : this;\n    }\n\n    public int select(int publicPriority, int protectedPriority, int defaultPriority, int privatePriority) {\n        switch(this) {\n        case PUBLIC:\n            return publicPriority;\n        case PROTECTED:\n            return protectedPriority;\n        case PACKAGE:\n            return defaultPriority;\n        case PRIVATE:\n            return privatePriority;\n        default:\n            throw new InternalError();\n        }\n    }\n\n    public static AccessLevel ofFlags(long flags) {\n        if (Flags.testAny(flags, Flags.PUBLIC))\n            return PUBLIC;\n        if (Flags.testAny(flags, Flags.PROTECTED))\n            return PROTECTED;\n        if (Flags.testAny(flags, Flags.PRIVATE))\n            return PRIVATE;\n        return PACKAGE;\n    }\n\n    public static AccessLevel of(MethodDefinition md) {\n        TypeDefinition td = md.getDeclaringType();\n        return ofFlags(td.getFlags()).min(ofFlags(md.getFlags()));\n    }\n    \n    public static AccessLevel of(FieldDefinition fd) {\n        TypeDefinition td = fd.getDeclaringType();\n        return ofFlags(td.getFlags()).min(ofFlags(fd.getFlags()));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Annotations.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport com.strobel.assembler.metadata.IAnnotationsProvider;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.assembler.metadata.annotations.CustomAnnotation;\n\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author shustkost\n *\n */\npublic class Annotations {\n    private static final String INTERNAL_ANNOTATION_PACKAGE = AssertWarning.class.getPackage().getName();\n\n    public static boolean hasAnnotation(IAnnotationsProvider fd, boolean ignoreDeprecated) {\n        for (CustomAnnotation ca : fd.getAnnotations()) {\n            TypeReference annoType = ca.getAnnotationType();\n            if (annoType.getPackageName().equals(INTERNAL_ANNOTATION_PACKAGE))\n                continue;\n            if (ignoreDeprecated && annoType.getInternalName().equals(\"java/lang/Deprecated\"))\n                continue;\n            String simpleName = annoType.getSimpleName();\n            if (simpleName.startsWith(\"Suppress\") && simpleName.endsWith(\"Warning\"))\n                continue;\n            if (simpleName.equalsIgnoreCase(\"nonnull\") || simpleName.equalsIgnoreCase(\"notnull\") || simpleName\n                    .equalsIgnoreCase(\"nullable\") || simpleName.equalsIgnoreCase(\"checkfornull\"))\n                continue;\n            return true;\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Equi.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.BiPredicate;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.assembler.metadata.VariableDefinition;\nimport com.strobel.core.StringUtilities;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.CaseBlock;\nimport com.strobel.decompiler.ast.CatchBlock;\nimport com.strobel.decompiler.ast.Condition;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Lambda;\nimport com.strobel.decompiler.ast.Loop;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.Switch;\nimport com.strobel.decompiler.ast.TryCatchBlock;\nimport com.strobel.decompiler.ast.Variable;\n\n/**\n * Utility methods to check the equivalence of nodes/expressions\n * \n * @author Tagir Valeev\n */\npublic class Equi {\n    static class EquiContext {\n        private static final int MAX_COUNT = 1000;\n        \n        int count;\n        BiPredicate<Variable, Variable> varMatcher = Variable::equals;\n        \n        boolean addAndCheck() {\n            if(++count > MAX_COUNT) {\n                return false;\n            }\n            return true;\n        }\n    }\n    \n    public static boolean equiBlocks(Block left, Block right) {\n        return equiBlocks(left, right, new EquiContext());\n    }\n    \n    private static boolean equiBlocks(Block left, Block right, EquiContext ctx) {\n        if (left == null)\n            return right == null;\n        if (!ctx.addAndCheck())\n            return false;\n        if (right == null)\n            return false;\n        List<Node> leftBody = left.getBody();\n        List<Node> rightBody = right.getBody();\n        if (leftBody.size() != rightBody.size())\n            return false;\n        for (int i = 0; i < leftBody.size(); i++) {\n            Node leftNode = leftBody.get(i);\n            Node rightNode = rightBody.get(i);\n            if (leftNode.getClass() != rightNode.getClass())\n                return false;\n        }\n        int start = left instanceof CatchBlock ? 1 : 0;\n        for (int i = start; i < leftBody.size(); i++) {\n            Node leftNode = leftBody.get(i);\n            Node rightNode = rightBody.get(i);\n            if (leftNode instanceof Expression) {\n                if (!equiExpressions((Expression) leftNode, (Expression) rightNode, ctx))\n                    return false;\n            } else if (leftNode instanceof Condition) {\n                Condition leftCond = (Condition) leftNode;\n                Condition rightCond = (Condition) rightNode;\n                if (!equiExpressions(leftCond.getCondition(), rightCond.getCondition(), ctx))\n                    return false;\n                if (!equiBlocks(leftCond.getTrueBlock(), rightCond.getTrueBlock(), ctx))\n                    return false;\n                if (!equiBlocks(leftCond.getFalseBlock(), rightCond.getFalseBlock(), ctx))\n                    return false;\n            } else if (leftNode instanceof Loop) {\n                Loop leftLoop = (Loop) leftNode;\n                Loop rightLoop = (Loop) rightNode;\n                if (leftLoop.getLoopType() != rightLoop.getLoopType())\n                    return false;\n                if (!equiExpressions(leftLoop.getCondition(), rightLoop.getCondition(), ctx))\n                    return false;\n                if (!equiBlocks(leftLoop.getBody(), rightLoop.getBody(), ctx))\n                    return false;\n            } else if (leftNode instanceof TryCatchBlock) {\n                TryCatchBlock leftTry = (TryCatchBlock) leftNode;\n                TryCatchBlock rightTry = (TryCatchBlock) rightNode;\n                if (!equiTryCatch(leftTry, rightTry, ctx))\n                    return false;\n            } else if (leftNode instanceof Switch) {\n                Switch leftSwitch = (Switch) leftNode;\n                Switch rightSwitch = (Switch) rightNode;\n                List<CaseBlock> leftCases = leftSwitch.getCaseBlocks();\n                List<CaseBlock> rightCases = rightSwitch.getCaseBlocks();\n                if(leftCases.size() != rightCases.size())\n                    return false;\n                if(!equiExpressions(leftSwitch.getCondition(), rightSwitch.getCondition(), ctx))\n                    return false;\n                for(int j=0; j<leftCases.size(); j++) {\n                    CaseBlock leftCase = leftCases.get(j);\n                    CaseBlock rightCase = rightCases.get(j);\n                    if(!leftCase.getValues().equals(rightCase.getValues()))\n                        return false;\n                    if(!equiBlocks(leftCase, rightCase, ctx))\n                        return false;\n                }\n            } else {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private static boolean equiTryCatch(TryCatchBlock leftTry, TryCatchBlock rightTry, EquiContext ctx) {\n        List<CatchBlock> leftCatches = leftTry.getCatchBlocks();\n        List<CatchBlock> rightCatches = rightTry.getCatchBlocks();\n        if (leftCatches.size() != rightCatches.size())\n            return false;\n        for (int j = 0; j < leftCatches.size(); j++) {\n            CatchBlock leftCatch = leftCatches.get(j);\n            CatchBlock rightCatch = rightCatches.get(j);\n            if (!equiTypes(leftCatch.getExceptionType(), rightCatch.getExceptionType()))\n                return false;\n            List<TypeReference> leftTypes = leftCatch.getCaughtTypes();\n            List<TypeReference> rightTypes = rightCatch.getCaughtTypes();\n            if (leftTypes.size() != rightTypes.size())\n                return false;\n            for (int k = 0; k < leftTypes.size(); k++) {\n                if (!equiTypes(leftTypes.get(k), rightTypes.get(k)))\n                    return false;\n            }\n            if (!equiBlocks(leftCatch, rightCatch, ctx))\n                return false;\n        }\n        if (!equiBlocks(leftTry.getTryBlock(), rightTry.getTryBlock(), ctx))\n            return false;\n        if (!equiBlocks(leftTry.getFinallyBlock(), rightTry.getFinallyBlock(), ctx))\n            return false;\n        return true;\n    }\n    \n    public static boolean equiExpressions(Expression left, Expression right) {\n        return equiExpressions(left, right, new EquiContext());\n    }\n    \n    private static boolean equiExpressions(Expression left, Expression right, EquiContext ctx) {\n        if (left == null)\n            return right == null;\n        if (!ctx.addAndCheck())\n            return false;\n        if (right == null)\n            return false;\n        if (left.getCode() != right.getCode()) {\n            if((left.getCode() == AstCode.CmpGe && right.getCode() == AstCode.CmpLe) ||\n                    (left.getCode() == AstCode.CmpGt && right.getCode() == AstCode.CmpLt) ||\n                    (left.getCode() == AstCode.CmpLe && right.getCode() == AstCode.CmpGe) ||\n                    (left.getCode() == AstCode.CmpLt && right.getCode() == AstCode.CmpGt)) {\n                return left.getArguments().size() == 2 && right.getArguments().size() == 2 &&\n                        equiExpressions(left.getArguments().get(0), right.getArguments().get(1), ctx) &&\n                        equiExpressions(left.getArguments().get(1), right.getArguments().get(0), ctx);\n            }\n            return false;\n        }\n        \n        Object leftOp = left.getOperand();\n        Object rightOp = right.getOperand();\n\n        if (!equiOperands(leftOp, rightOp, ctx))\n            return false;\n\n        if (left.getArguments().size() != right.getArguments().size()) {\n            return false;\n        }\n        \n        if (left.getArguments().size() == 2) {\n            // Commutative operators check\n            switch(left.getCode()) {\n            case And:\n            case Or:\n            case Xor:\n            case Add:\n            case Mul:\n            case CmpEq:\n            case CmpNe:\n                return (equiExpressions(left.getArguments().get(0), right.getArguments().get(0), ctx) &&\n                        equiExpressions(left.getArguments().get(1), right.getArguments().get(1), ctx)) ||\n                        (equiExpressions(left.getArguments().get(0), right.getArguments().get(1), ctx) &&\n                                equiExpressions(left.getArguments().get(1), right.getArguments().get(0), ctx));\n            default:\n            }\n        }\n\n        for (int i = 0, n = left.getArguments().size(); i < n; i++) {\n            final Expression a1 = left.getArguments().get(i);\n            final Expression a2 = right.getArguments().get(i);\n\n            if (!equiExpressions(a1, a2, ctx)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    private static boolean equiOperands(Object left, Object right, EquiContext ctx) {\n        if (left == null)\n            return right == null;\n        if (right == null)\n            return false;\n        if (left instanceof FieldReference) {\n            if (!(right instanceof FieldReference))\n                return false;\n            return equiFields((FieldReference) left, (FieldReference) right);\n        }\n        if (left instanceof MethodReference) {\n            if (!(right instanceof MethodReference))\n                return false;\n            return equiMethods((MethodReference) left, (MethodReference) right);\n        }\n        if (left instanceof Lambda) {\n            if(right.getClass() != left.getClass())\n                return false;\n            return equiLambdas((Lambda)left, (Lambda)right, ctx);\n        }\n        if (left instanceof Variable) {\n            if(right.getClass() != left.getClass())\n                return false;\n            return ctx.varMatcher.test((Variable)left, (Variable)right);\n        }\n        return Objects.equals(right, left);\n    }\n\n    private static boolean equiMethods(final MethodReference left, final MethodReference right) {\n        return StringUtilities.equals(left.getFullName(), right.getFullName()) &&\n            StringUtilities.equals(left.getErasedSignature(), right.getErasedSignature());\n    }\n\n    private static boolean equiFields(final FieldReference left, final FieldReference right) {\n        return StringUtilities.equals(left.getFullName(), right.getFullName());\n    }\n\n    private static boolean equiLambdas(Lambda left, Lambda right, EquiContext ctx) {\n        if(!equiMethods(left.getMethod(), right.getMethod())\n                || !equiTypes(left.getFunctionType(), right.getFunctionType()))\n            return false;\n        MethodDefinition leftMd = Nodes.getLambdaMethod(left);\n        MethodDefinition rightMd = Nodes.getLambdaMethod(right);\n        BiPredicate<Variable, Variable> curMatcher = ctx.varMatcher;\n        ctx.varMatcher = (vl, vr) -> {\n            if(curMatcher.test(vl, vr))\n                return true;\n            VariableDefinition vlo = vl.getOriginalVariable();\n            VariableDefinition vro = vr.getOriginalVariable();\n            if(vlo != null && vro != null) {\n                if(Variables.getMethodDefinition(vlo) == leftMd && Variables.getMethodDefinition(vro) == rightMd) {\n                    return vlo.getVariableType().isEquivalentTo(vro.getVariableType()) && vlo.getSlot() == vro.getSlot();\n                }\n            }\n            ParameterDefinition pl = vl.getOriginalParameter();\n            ParameterDefinition pr = vr.getOriginalParameter();\n            if(pl != null && pr != null && pl.getMethod() == leftMd && pr.getMethod() == rightMd) {\n                return pl.getPosition() == pr.getPosition();\n            }\n            return false;\n        };\n        boolean result = equiBlocks(left.getBody(), right.getBody(), ctx);\n        ctx.varMatcher = curMatcher;\n        return result;\n    }\n\n    private static boolean equiTypes(TypeReference left, TypeReference right) {\n        if (left == null)\n            return right == null;\n        if (right == null)\n            return false;\n        return left.getInternalName().equals(right.getInternalName());\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/ExpressionFormatter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport one.util.huntbugs.warning.Formatter;\n\nimport com.strobel.assembler.metadata.DynamicCallSite;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodHandle;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\n/**\n * @author lan\n *\n */\npublic class ExpressionFormatter {\n    public static final int DEF_PRECEDENCE = 100;\n\n    public static String formatExpression(Expression expr) {\n        return format(new StringBuilder(), expr, DEF_PRECEDENCE).toString();\n    }\n\n    private static StringBuilder format(StringBuilder sb, Expression expr, int outerPrecedence) {\n        switch(expr.getCode()) {\n        case TernaryOp:\n            return format(format(format(sb, expr.getArguments().get(0), DEF_PRECEDENCE).append(\" ? \"), expr\n                    .getArguments().get(1), DEF_PRECEDENCE).append(\" : \"), expr.getArguments().get(2), DEF_PRECEDENCE);\n        case And:\n            return formatBinary(sb, expr, \" & \", 9, outerPrecedence);\n        case Or:\n            return formatBinary(sb, expr, \" | \", 11, outerPrecedence);\n        case Xor:\n            return formatBinary(sb, expr, \" ^ \", 10, outerPrecedence);\n        case Not:\n            return formatUnary(sb, expr, \"~\", \"\", 2, outerPrecedence);\n        case Neg:\n            return formatUnary(sb, expr, \"-\", \"\", 2, outerPrecedence);\n        case LogicalAnd:\n            return formatBinary(sb, expr, \" && \", 12, outerPrecedence);\n        case LogicalOr:\n            return formatBinary(sb, expr, \" || \", 13, outerPrecedence);\n        case LogicalNot:\n            return formatUnary(sb, expr, \"!\", \"\", 2, outerPrecedence);\n        case Add:\n            return formatBinary(sb, expr, \" + \", 5, outerPrecedence);\n        case Sub:\n            return formatBinary(sb, expr, \" - \", 5, outerPrecedence);\n        case Mul:\n            return formatBinary(sb, expr, \" * \", 4, outerPrecedence);\n        case Div:\n            return formatBinary(sb, expr, \" / \", 4, outerPrecedence);\n        case Rem:\n            return formatBinary(sb, expr, \" % \", 4, outerPrecedence);\n        case Shr:\n            return formatBinary(sb, expr, \" >> \", 6, outerPrecedence);\n        case Shl:\n            return formatBinary(sb, expr, \" << \", 6, outerPrecedence);\n        case UShr:\n            return formatBinary(sb, expr, \" >>> \", 6, outerPrecedence);\n        case CmpEq:\n            return formatBinary(sb, expr, \" == \", 8, outerPrecedence);\n        case CmpNe:\n            return formatBinary(sb, expr, \" != \", 8, outerPrecedence);\n        case CmpLt:\n            return formatBinary(sb, expr, \" < \", 7, outerPrecedence);\n        case CmpLe:\n            return formatBinary(sb, expr, \" <= \", 7, outerPrecedence);\n        case CmpGt:\n            return formatBinary(sb, expr, \" > \", 7, outerPrecedence);\n        case CmpGe:\n            return formatBinary(sb, expr, \" >= \", 7, outerPrecedence);\n        case GetField: {\n            FieldReference fr = (FieldReference) expr.getOperand();\n            return formatUnary(sb, expr, \"\", \".\"+fr.getName(), 1, outerPrecedence);\n        }\n        case PutField: {\n            FieldReference fr = (FieldReference) expr.getOperand();\n            return formatBinary(sb, expr, \".\"+fr.getName()+\" = \", 15, outerPrecedence);\n        }\n        case GetStatic: {\n            FieldReference fr = (FieldReference) expr.getOperand();\n            return sb.append(fr.getDeclaringType().getSimpleName()).append(\".\").append(fr.getName());\n        }\n        case PutStatic: {\n            FieldReference fr = (FieldReference) expr.getOperand();\n            return formatUnary(sb, expr, fr.getDeclaringType().getSimpleName() + \".\" + fr.getName() + \" = \", \"\", 15,\n                outerPrecedence);\n        }\n        case Return:\n            return formatUnary(sb, expr, \"return \", \"\", DEF_PRECEDENCE, outerPrecedence);\n        case AThrow:\n            return formatUnary(sb, expr, \"throw \", \"\", DEF_PRECEDENCE, outerPrecedence);\n        case ArrayLength:\n            return formatUnary(sb, expr, \"\", \".length\", 1, outerPrecedence);\n        case LoadElement:\n            return format(format(sb, expr.getArguments().get(0), outerPrecedence).append(\"[\"), expr.getArguments().get(1), outerPrecedence).append(\"]\");\n        case StoreElement:\n            return format(format(format(sb, expr.getArguments().get(0), outerPrecedence).append(\"[\"), expr.getArguments().get(1), outerPrecedence)\n                    .append(\"] = \"), expr.getArguments().get(2), outerPrecedence);\n        case Load: {\n            Object op = expr.getOperand();\n            return sb.append(op instanceof Variable ? ((Variable) op).getName() : ((ParameterDefinition) op).getName());\n        }\n        case Store:\n            return formatUnary(sb, expr, ((Variable)expr.getOperand()).getName()+ \" = \", \"\", 15, outerPrecedence);\n        case I2B:\n            return formatUnary(sb, expr, \"(byte)\", \"\", 3, outerPrecedence);\n        case I2C:\n            return formatUnary(sb, expr, \"(char)\", \"\", 3, outerPrecedence);\n        case I2S:\n            return formatUnary(sb, expr, \"(short)\", \"\", 3, outerPrecedence);\n        case I2F:\n        case L2F:\n        case D2F:\n            return formatUnary(sb, expr, \"(float)\", \"\", 3, outerPrecedence);\n        case F2I:\n        case D2I:\n        case L2I:\n            return formatUnary(sb, expr, \"(int)\", \"\", 3, outerPrecedence);\n        case F2L:\n        case D2L:\n            return formatUnary(sb, expr, \"(long)\", \"\", 3, outerPrecedence);\n        case I2D:\n        case I2L:\n        case L2D:\n        case F2D:\n            return format(sb, expr.getArguments().get(0), outerPrecedence);\n        case CheckCast:\n            return formatUnary(sb, expr, \"(\"+((TypeReference)expr.getOperand()).getSimpleName()+\")(\", \")\", 3, outerPrecedence);\n        case InstanceOf:\n            return formatUnary(sb, expr, \"\", \" instanceof \"+((TypeReference)expr.getOperand()).getSimpleName(), 7, outerPrecedence);\n        case InvokeStatic: {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            if(Nodes.isBoxing(expr))\n                return formatUnary(sb, expr, \"(\"+mr.getDeclaringType().getSimpleName()+\")(\", \")\", 3, outerPrecedence);\n            sb.append(mr.getDeclaringType().getSimpleName()).append(\".\").append(mr.getName()).append(\"(\");\n            boolean firstArg = true;\n            for(Expression child : expr.getArguments()) {\n                if(!firstArg) {\n                    sb.append(\", \");\n                } else {\n                    firstArg = false;\n                }\n                format(sb, child, DEF_PRECEDENCE);\n            }\n            return sb.append(\")\");\n        }\n        case InitObject: {\n            MethodReference mr = (MethodReference) expr.getOperand();\n            sb.append(\"new \").append(mr.getDeclaringType().getSimpleName()).append(\"(\");\n            boolean firstArg = true;\n            for(Expression child : expr.getArguments()) {\n                if(!firstArg) {\n                    sb.append(\", \");\n                } else {\n                    firstArg = false;\n                }\n                format(sb, child, DEF_PRECEDENCE);\n            }\n            return sb.append(\")\");\n        }\n        case AConstNull:\n            return sb.append(\"null\");\n        case InvokeSpecial:\n        case InvokeInterface:\n        case InvokeVirtual: {\n            if(Nodes.isUnboxing(expr))\n                return format(sb, expr.getArguments().get(0), outerPrecedence);\n            MethodReference mr = (MethodReference) expr.getOperand();\n            format(sb, expr.getArguments().get(0), 1);\n            sb.append(\".\").append(mr.getName()).append(\"(\");\n            boolean firstArg = true;\n            for(int i=1; i<expr.getArguments().size(); i++) {\n                Expression child = expr.getArguments().get(i);\n                if(!firstArg) {\n                    sb.append(\", \");\n                } else {\n                    firstArg = false;\n                }\n                format(sb, child, DEF_PRECEDENCE);\n            }\n            return sb.append(\")\");\n        }\n        case InvokeDynamic: {\n            MethodHandle mh = Nodes.getMethodHandle((DynamicCallSite)expr.getOperand());\n            if(mh != null) {\n                MethodReference mr = mh.getMethod();\n                return sb.append(mr.getDeclaringType().getSimpleName()).append(\"::\").append(mr.getName());\n            }\n            return sb.append(expr.toString());\n        }\n        case LdC: {\n            return sb.append(Formatter.formatConstant(expr.getOperand()));\n        }\n        case PreIncrement: {\n            Object op = expr.getOperand();\n            if(op instanceof Integer) {\n                int amount = (int)op;\n                if(amount == 1) {\n                    return formatUnary(sb, expr, \"++\", \"\", 2, outerPrecedence);\n                } else if(amount == -1) {\n                    return formatUnary(sb, expr, \"--\", \"\", 2, outerPrecedence);\n                }\n            }\n            return sb.append(expr.toString());\n        }\n        case PostIncrement: {\n            Object op = expr.getOperand();\n            if(op instanceof Integer) {\n                int amount = (int)op;\n                if(amount == 1) {\n                    return formatUnary(sb, expr, \"\", \"++\", 2, outerPrecedence);\n                } else if(amount == -1) {\n                    return formatUnary(sb, expr, \"\", \"--\", 2, outerPrecedence);\n                }\n            }\n            return sb.append(expr.toString());\n        }\n        default: \n            return sb.append(expr.toString());\n        }\n    }\n\n    private static StringBuilder formatUnary(StringBuilder sb, Expression expr, String prefix, String postfix, int precedence, int outerPrecedence) {\n        if(precedence > outerPrecedence) {\n            return format(sb.append(\"(\").append(prefix), expr.getArguments().get(0), precedence).append(postfix).append(\")\");\n        }\n        return format(sb.append(prefix), expr.getArguments().get(0), precedence).append(postfix);\n    }\n\n    private static StringBuilder formatBinary(StringBuilder sb, Expression expr, String op, int precedence, int outerPrecedence) {\n        if(precedence > outerPrecedence) {\n            return format(\n                format(sb.append(\"(\"), expr.getArguments().get(0), precedence).append(op),\n                expr.getArguments().get(1), precedence).append(\")\");\n        }\n        return format(format(sb, expr.getArguments().get(0), precedence).append(op), expr\n                .getArguments().get(1), precedence);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Exprs.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.ValuesFlow;\n\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.assembler.metadata.VariableDefinition;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\n/**\n * Expression-related utility methods\n * \n * @author Tagir Valeev\n */\npublic class Exprs {\n\n    private static boolean isAssertionStatusCheck(Expression expr) {\n        if(expr.getCode() != AstCode.LogicalNot)\n            return false;\n        Expression arg = expr.getArguments().get(0);\n        if(arg.getCode() != AstCode.GetStatic)\n            return false;\n        FieldReference fr = (FieldReference) arg.getOperand();\n        return fr.getName().startsWith(\"$assertions\");\n    }\n\n    private static boolean isAssertionCondition(Expression expr) {\n        if(expr.getCode() != AstCode.LogicalAnd)\n            return false;\n        return expr.getArguments().stream().anyMatch(Exprs::isAssertionStatusCheck);\n    }\n\n    private static boolean isAssertionMethod(Expression expr) {\n        if(expr.getCode() != AstCode.InvokeStatic)\n            return false;\n        MethodReference mr = (MethodReference) expr.getOperand();\n        String name = mr.getName();\n        return name.equals(\"isTrue\") || name.equals(\"assertTrue\");\n    }\n\n    public static boolean isAssertion(Expression expr) {\n        Set<Expression> usages = Inf.BACKLINK.findUsages(expr);\n        return !usages.isEmpty() && usages.stream().allMatch(parent -> isAssertionCondition(parent) || isAssertion(parent) || isAssertionMethod(parent));\n    }\n\n    public static Expression getChild(Expression node, int i) {\n        return ValuesFlow.getSource(node.getArguments().get(i));\n    }\n\n    public static Expression getChildNoSpecial(Expression node, int i) {\n        Expression arg = node.getArguments().get(i);\n        Expression src = ValuesFlow.getSource(arg);\n        if(ValuesFlow.isSpecial(src))\n            return arg;\n        return src;\n    }\n\n    public static Expression getThis(Expression node) {\n        if (node.getCode() == AstCode.GetField || node.getCode() == AstCode.PutField)\n            return node.getArguments().get(0);\n        if (node.getCode() == AstCode.GetStatic || node.getCode() == AstCode.PutStatic)\n            return null;\n        throw new IllegalArgumentException(node + \": expected field operation\");\n    }\n\n    public static Expression findExpression(Expression node, Predicate<Expression> predicate) {\n        if (predicate.test(node))\n            return node;\n        for (Expression child : node.getArguments()) {\n            Expression result = findExpression(child, predicate);\n            if (result != null)\n                return result;\n        }\n        return null;\n    }\n\n    public static Expression findExpressionWithSources(Expression node, Predicate<Expression> predicate) {\n        return findExpressionWithSources(node, new HashSet<>(), predicate);\n    }\n\n    private static Expression findExpressionWithSources(Expression node, Set<Expression> visited, Predicate<Expression> predicate) {\n        if (predicate.test(node))\n            return node;\n        for (Expression child : node.getArguments()) {\n            Expression result = findExpressionWithSources(child, visited, predicate);\n            if (result != null)\n                return result;\n        }\n        Expression source = ValuesFlow.getSource(node);\n        if (source != node && visited.add(source)) {\n            Expression result = findExpressionWithSources(source, visited, predicate);\n            if (result != null)\n                return result;\n        }\n        return null;\n    }\n    \n    public static boolean isThis(Expression self) {\n        if (self.getCode() != AstCode.Load)\n            return false;\n        if (self.getOperand() instanceof Variable) {\n            VariableDefinition origVar = ((Variable) self.getOperand()).getOriginalVariable();\n            return origVar != null && origVar.getSlot() == 0;\n        }\n        if (self.getOperand() instanceof ParameterDefinition) {\n            ParameterDefinition pd = (ParameterDefinition) self.getOperand();\n            return pd.getSlot() == 0;\n        }\n        return false;\n    }\n\n    public static boolean isParameter(Expression expr) {\n        if (expr.getCode() == AstCode.Load) {\n            if (expr.getOperand() instanceof ParameterDefinition)\n                return true;\n            if (expr.getOperand() instanceof Variable && ((Variable) expr.getOperand()).getOriginalParameter() != null)\n                return true;\n        }\n        return false;\n    }\n\n    public static Stream<Expression> stream(Expression expr) {\n        return Stream.concat(Stream.of(expr), expr.getArguments().stream().flatMap(Exprs::stream));\n    }\n\n    public static boolean bothMatch(Expression e1, Expression e2, Predicate<Expression> p1, Predicate<Expression> p2) {\n        return p1.test(e1) && p2.test(e2) || p1.test(e2) && p2.test(e1);\n    }\n\n    public static boolean bothChildrenMatch(Expression e, Predicate<Expression> p1, Predicate<Expression> p2) {\n        List<Expression> args = e.getArguments();\n        if(args.size() != 2)\n            throw new IllegalArgumentException(\"Children size = \"+args.size()+\"; expr = \"+e);\n        return bothMatch(ValuesFlow.getSource(args.get(0)), ValuesFlow.getSource(args.get(1)), p1, p2);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Iterables.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * @author lan\n *\n */\npublic class Iterables {\n    public static <T> Iterable<T> concat(Iterable<? extends T> it1, Iterable<? extends T> it2) {\n        return () -> new Iterator<T>() {\n            boolean first = true;\n            Iterator<? extends T> it = it1.iterator();\n\n            @Override\n            public boolean hasNext() {\n                boolean hasNext = it.hasNext();\n                if(hasNext)\n                    return true;\n                if(first) {\n                    first = false;\n                    it = it2.iterator();\n                    return hasNext();\n                }\n                return false;\n            }\n\n            @Override\n            public T next() {\n                if(first) {\n                    hasNext();\n                }\n                return it.next();\n            }\n        };\n    }\n    \n    public static <T> List<T> toList(Iterable<T> iterable) {\n        List<T> list = new ArrayList<>();\n        iterable.forEach(list::add);\n        return list;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Maps.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\n/**\n * @author lan\n *\n */\npublic class Maps {\n\n    public static <K, V> Map<K, V> compactify(Map<K, V> map) {\n        if (map.isEmpty())\n            return Collections.emptyMap();\n        if (map.size() == 1) {\n            Entry<K, V> entry = map.entrySet().iterator().next();\n            return Collections.singletonMap(entry.getKey(), entry.getValue());\n        }\n        return map;\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Methods.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport com.strobel.assembler.ir.Instruction;\nimport com.strobel.assembler.ir.OpCode;\nimport com.strobel.assembler.metadata.MethodBody;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.ParameterDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\n\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class Methods {\n    private static final Set<String> SERIALIZATION_METHODS = \n            new HashSet<>(Arrays.asList(\"writeReplace\", \"readResolve\",\n                \"readObject\", \"readObjectNoData\", \"writeObject\"));\n    \n    public static boolean isEqualsMethod(MethodReference mr) {\n        return mr.getName().equals(\"equals\") && mr.getSignature().equals(\"(Ljava/lang/Object;)Z\");\n    }\n\n    public static boolean isHashCodeMethod(MethodReference mr) {\n        return mr.getName().equals(\"hashCode\") && mr.getSignature().equals(\"()I\");\n    }\n    \n    public static boolean isGetClass(MethodReference mr) {\n        return mr.getName().equals(\"getClass\") && mr.getErasedSignature().equals(\"()Ljava/lang/Class;\");\n    }\n    \n    public static boolean is(MethodReference mr, String internalTypeName, String methodName, String methodSig) {\n        return mr.getName().equals(methodName) && mr.getErasedSignature().equals(methodSig) &&\n                mr.getDeclaringType().getInternalName().equals(internalTypeName);\n    }\n    \n    public static boolean knownToThrow(MethodReference mr) {\n        if (mr.getName().startsWith(\"assert\") || mr.getName().startsWith(\"require\")\n            || mr.getDeclaringType().getSimpleName().equals(\"Assert\"))\n            return true;\n        if (Types.isBoxed(mr.getDeclaringType()) && mr.getName().startsWith(\"parse\"))\n            return true;\n        return false;\n    }\n    \n    public static MethodDefinition findSuperMethod(MethodReference mr) {\n        MethodDefinition md = mr.resolve();\n        if(md == null)\n            return null;\n        TypeDefinition td = md.getDeclaringType();\n        return findSuperMethod(td, new MemberInfo(resolveToBridge(md)));\n    }\n    \n    public static Set<MethodDefinition> findSuperMethods(MethodReference mr) {\n        MethodDefinition md = mr.resolve();\n        if(md == null)\n            return null;\n        TypeDefinition td = md.getDeclaringType();\n        Set<MethodDefinition> set = new HashSet<>();\n        collectSuperMethods(td, new MemberInfo(resolveToBridge(md)), set);\n        return set;\n    }\n    \n    private static void collectSuperMethods(TypeDefinition type, MemberInfo mi, Set<MethodDefinition> list) {\n        TypeReference superType = type.getBaseType();\n        if(superType != null) {\n            TypeDefinition superTd = superType.resolve();\n            if(superTd != null) {\n                MethodDefinition result = findMethod(superTd, mi);\n                if(result != null)\n                    list.add(result);\n                else\n                    collectSuperMethods(superTd, mi, list);\n            }\n        }\n        for(TypeReference iface : type.getExplicitInterfaces()) {\n            TypeDefinition ifaceTd = iface.resolve();\n            if(ifaceTd != null) {\n                MethodDefinition result = findMethod(ifaceTd, mi);\n                if(result != null)\n                    list.add(result);\n                else\n                    collectSuperMethods(ifaceTd, mi, list);\n            }\n        }\n    }\n\n    public static MethodDefinition resolveToBridge(MethodDefinition md) {\n        if (md.isBridgeMethod()) {\n            return md;\n        }\n        for (MethodDefinition candidate : md.getDeclaringType().getDeclaredMethods()) {\n            if (candidate.getName().equals(md.getName()) && candidate.isBridgeMethod()) {\n                List<ParameterDefinition> params = candidate.getParameters();\n                if (params.size() == md.getParameters().size()) {\n                    MethodBody body = candidate.getBody();\n                    if (body != null) {\n                        for (Instruction instr : body.getInstructions()) {\n                            if (instr.getOperandCount() == 1) {\n                                Object operand = instr.getOperand(0);\n                                if (operand instanceof MethodReference) {\n                                    MethodReference mr = (MethodReference) operand;\n                                    if (mr.getName().equals(md.getName()) && mr.getErasedSignature().equals(md\n                                            .getErasedSignature()) && mr.getDeclaringType().isEquivalentTo(md\n                                                    .getDeclaringType())) {\n                                        return candidate;\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return md;\n    }\n    \n    public static MethodDefinition findSuperMethod(TypeDefinition type, MemberInfo mi) {\n        TypeReference superType = type.getBaseType();\n        if(superType != null) {\n            TypeDefinition superTd = superType.resolve();\n            if(superTd != null) {\n                MethodDefinition result = findMethod(superTd, mi);\n                if(result != null)\n                    return result;\n                result = findSuperMethod(superTd, mi);\n                if(result != null)\n                    return result;\n            }\n        }\n        for(TypeReference iface : type.getExplicitInterfaces()) {\n            TypeDefinition ifaceTd = iface.resolve();\n            if(ifaceTd != null) {\n                MethodDefinition result = findMethod(ifaceTd, mi);\n                if(result != null)\n                    return result;\n                result = findSuperMethod(ifaceTd, mi);\n                if(result != null)\n                    return result;\n            }\n        }\n        return null; \n    }\n\n    public static MethodDefinition findMethod(TypeDefinition td, MemberInfo mi) {\n        if(td == null)\n            return null;\n        for(MethodDefinition decl : td.getDeclaredMethods()) {\n            if(decl.getName().equals(mi.getName())) {\n                String sig1 = decl.getErasedSignature();\n                String sig2 = mi.getSignature();\n                if(sig1 == sig2)\n                    return decl;\n                if(sig1.substring(0, sig1.indexOf(')')).equals(sig2.substring(0, sig2.indexOf(')'))))\n                    return decl;\n            }\n        }\n        return null;\n    }\n\n    public static boolean isMain(MethodDefinition md) {\n        return md.getName().equals(\"main\") && md.isPublic() && md.isStatic() && md.getErasedSignature().startsWith(\"([Ljava/lang/String;)\");\n    }\n\n    public static boolean isSideEffectFree(MethodReference mr) {\n        if(isPure(mr))\n            return true;\n        if(isEqualsMethod(mr) || isHashCodeMethod(mr))\n            return true;\n        TypeReference tr = mr.getDeclaringType();\n        String sig = mr.getErasedSignature();\n        String name = mr.getName();\n        if(name.equals(\"toString\") && sig.equals(\"()Ljava/lang/String;\"))\n            return true;\n        switch(tr.getInternalName()) {\n        case \"java/util/Arrays\":\n            return name.equals(\"hashCode\") || name.equals(\"equals\") || name.equals(\"toString\")\n                || name.equals(\"binarySearch\") || name.equals(\"stream\")\n                || name.equals(\"spliterator\") || name.startsWith(\"deep\")\n                || name.startsWith(\"copyOf\") || name.equals(\"asList\");\n        case \"java/lang/Object\":\n            return mr.isConstructor();\n        case \"java/util/Collections\":\n            return name.equals(\"min\") || name.equals(\"max\") || name.startsWith(\"unmodifiable\") || name.startsWith(\"synchronized\")\n                    || name.startsWith(\"empty\");\n        }\n        if(Types.isCollection(tr)) {\n            if(name.equals(\"contains\") && sig.equals(\"(Ljava/lang/Object;)Z\"))\n                return true;\n            if(name.equals(\"containsAll\") && sig.equals(\"(Ljava/util/Collection;)Z\"))\n                return true;\n            if(name.equals(\"isEmpty\") && sig.equals(\"()Z\"))\n                return true;\n            if(name.equals(\"size\") && sig.equals(\"()I\"))\n                return true;\n            if(Types.isInstance(tr, \"java/util/List\")) {\n                if (name.equals(\"get\") && sig.equals(\"(I)Ljava/lang/Object;\") || name.equals(\"subList\")\n                    && sig.equals(\"(II)Ljava/util/List;\"))\n                    return true;\n            }\n            return false;\n        }\n        if(Types.isInstance(tr, \"java/util/Map\")) {\n            if ((name.equals(\"containsKey\") || name.equals(\"containsValue\"))\n                && sig.equals(\"(Ljava/lang/Object;)Z\"))\n                return true;\n            if (name.equals(\"get\") && sig.equals(\"(Ljava/lang/Object;)Ljava/lang/Object;\"))\n                return true;\n        }\n        return Types.isSideEffectFreeType(tr);\n    }\n\n    public static boolean isPure(MethodReference mr) {\n        TypeReference tr = mr.getDeclaringType();\n        if(Types.isBoxed(tr) || tr.getInternalName().startsWith(\"java/time/\"))\n            return true;\n        if(tr.getInternalName().equals(\"java/util/String\"))\n            return !mr.getName().equals(\"getChars\");\n        if(tr.getInternalName().equals(\"java/lang/Math\"))\n            return !mr.getName().equals(\"random\");\n        if(tr.getInternalName().equals(\"java/util/Objects\"))\n            return true;\n        if(tr.getInternalName().equals(\"java/util/Optional\"))\n            return mr.getName().equals(\"get\") || mr.getName().equals(\"orElse\") || mr.getName().equals(\"isPresent\");\n        return false;\n    }\n\n    public static boolean isThrower(MethodDefinition md) {\n        MethodBody body = md.getBody();\n        if(body == null)\n            return false;\n        for(Instruction inst : body.getInstructions()) {\n            if(inst.hasLabel() || inst.getOpCode() == OpCode.RETURN || inst.getOpCode() == OpCode.ARETURN)\n                return false;\n            if(inst.getOpCode() == OpCode.ATHROW)\n                return true;\n        }\n        // Actually should not go here for valid bytecode\n        return false;\n    }\n\n    public static boolean isSerializationMethod(MethodDefinition md) {\n        return SERIALIZATION_METHODS.contains(md.getName());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/NodeChain.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Objects;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.CatchBlock;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Lambda;\nimport com.strobel.decompiler.ast.Loop;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.TryCatchBlock;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class NodeChain {\n    private final NodeChain parent;\n    private final Node cur;\n\n    public NodeChain(NodeChain parent, Node cur) {\n        this.parent = parent;\n        this.cur = Objects.requireNonNull(cur);\n    }\n\n    public NodeChain getParent() {\n        return parent;\n    }\n\n    public Node getNode() {\n        return cur;\n    }\n    \n    @Override\n    public String toString() {\n        if(parent == null)\n            return cur.toString();\n        return cur + \" -> \"+parent;\n    }\n    \n    /**\n     * @return root block of this NodeChain\n     */\n    public Block getRoot() {\n        NodeChain nc = this;\n        while(nc.getParent() != null) {\n            nc = nc.getParent();\n        }\n        return (Block) nc.getNode();\n    }\n    \n    /**\n     * @return true if this NodeChain inside the synchronized block\n     */\n    public boolean isSynchronized() {\n        NodeChain chain = this;\n        while(chain != null) {\n            if(Nodes.isSynchorizedBlock(chain.getNode()))\n                return true;\n            chain = chain.getParent();\n        }\n        return false;\n    }\n    \n    /**\n     * @return Expression which is used for synchronization if this NodeChain directly represents synchronized try block.\n     * Otherwise returns null.\n     */\n    public Expression getSyncObject() {\n        if(cur instanceof TryCatchBlock && parent != null) {\n            Node parentNode = parent.getNode();\n            if(parentNode instanceof Block) {\n                List<Node> peers = ((Block)parentNode).getBody();\n                for(int i = 1; i < peers.size(); i++) {\n                    if(peers.get(i) == cur) {\n                        Node prev = peers.get(i-1);\n                        if(Nodes.isOp(prev, AstCode.MonitorEnter)) {\n                            return ((Expression)prev).getArguments().get(0);\n                            //Exprs.getChild((Expression) prev, 0);\n                        }\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    public boolean isInTry(String... wantedExceptions) {\n        NodeChain nc = this;\n        while(nc != null) {\n            if(nc.getNode() instanceof Block && nc.getParent() != null && nc.getParent().getNode() instanceof TryCatchBlock) {\n                TryCatchBlock tcb = (TryCatchBlock) nc.getParent().getNode();\n                for(CatchBlock catchBlock : tcb.getCatchBlocks()) {\n                    TypeReference exType = catchBlock.getExceptionType();\n                    if(exType != null && Arrays.stream(wantedExceptions).anyMatch(exType.getInternalName()::equals))\n                        return true;\n                    if(catchBlock.getCaughtTypes().stream().anyMatch(t -> \n                        Arrays.stream(wantedExceptions).anyMatch(t.getInternalName()::equals)))\n                        return true;\n                }\n            }\n            nc = nc.getParent();\n        }\n        return false;\n    }\n    \n    public boolean isInCatch(String wantedException) {\n        NodeChain nc = this;\n        while(nc != null) {\n            if(nc.getNode() instanceof CatchBlock) {\n                CatchBlock catchBlock = (CatchBlock)nc.getNode();\n                TypeReference exType = catchBlock.getExceptionType();\n                if(exType != null && Types.isInstance(exType, wantedException))\n                    return true;\n                if(catchBlock.getCaughtTypes().stream().anyMatch(t -> Types.isInstance(t, wantedException)))\n                    return true;\n            }\n            nc = nc.getParent();\n        }\n        return false;\n    }\n    \n    public MethodDefinition getLambdaMethod() {\n        NodeChain nc = this;\n        while(nc != null) {\n            if(nc.getNode() instanceof Lambda) {\n                return Nodes.getLambdaMethod((Lambda) nc.getNode());\n            }\n            nc = nc.getParent();\n        }\n        return null;\n    }\n\n    public boolean isOnlyChild(Node node) {\n        Iterator<Node> iterator = Nodes.getChildren(getNode()).iterator();\n        return iterator.hasNext() && iterator.next() == node && !iterator.hasNext();\n    }\n\n    public boolean isInLoop() {\n        NodeChain chain = this;\n        while(chain != null) {\n            if(chain.getNode() instanceof Loop)\n                return true;\n            chain = chain.getParent();\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Nodes.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\nimport java.util.function.Predicate;\n\nimport one.util.huntbugs.flow.Annotators;\nimport one.util.huntbugs.flow.Inf;\nimport one.util.huntbugs.flow.PurityAnnotator.Purity;\nimport one.util.huntbugs.flow.ValuesFlow;\n\nimport com.strobel.assembler.metadata.DynamicCallSite;\nimport com.strobel.assembler.metadata.FieldReference;\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.MethodHandle;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Block;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Lambda;\nimport com.strobel.decompiler.ast.Node;\nimport com.strobel.decompiler.ast.TryCatchBlock;\nimport com.strobel.decompiler.ast.Variable;\n\n/**\n * Nodes-related utility methods\n * \n * @author Tagir Valeev\n * @see Exprs\n */\npublic class Nodes {\n    public static boolean isOp(Node node, AstCode op) {\n        return node instanceof Expression && ((Expression) node).getCode() == op;\n    }\n\n    public static boolean isInvoke(Node node) {\n        if (!(node instanceof Expression))\n            return false;\n        AstCode code = ((Expression) node).getCode();\n        return code == AstCode.InvokeDynamic || code == AstCode.InvokeStatic || code == AstCode.InvokeSpecial\n            || code == AstCode.InvokeVirtual || code == AstCode.InvokeInterface;\n    }\n\n    public static boolean isNullCheck(Node node) {\n        if (!isOp(node, AstCode.CmpEq) && !isOp(node, AstCode.CmpNe))\n            return false;\n        List<Expression> args = ((Expression) node).getArguments();\n        return args.get(0).getCode() == AstCode.AConstNull ^ args.get(1).getCode() == AstCode.AConstNull;\n    }\n\n    public static boolean isFieldRead(Node node) {\n        return isOp(node, AstCode.GetStatic) || isOp(node, AstCode.GetField);\n    }\n\n    public static Node getChild(Node node, int i) {\n        if (node instanceof Expression) {\n            return ValuesFlow.getSource(((Expression) node).getArguments().get(i));\n        }\n        return node.getChildren().get(i);\n    }\n\n    public static Object getConstant(Node node) {\n        if (!(node instanceof Expression))\n            return null;\n        Expression expr = (Expression) node;\n        if (expr.getCode() == AstCode.LdC)\n            return expr.getOperand();\n        return Inf.CONST.getValue(expr);\n    }\n\n    public static void ifBinaryWithConst(Expression expr, BiConsumer<Expression, Object> consumer) {\n        if (expr.getArguments().size() == 2) {\n            Expression left = expr.getArguments().get(0);\n            Expression right = expr.getArguments().get(1);\n            Object constant = getConstant(left);\n            if (constant != null) {\n                consumer.accept(right, constant);\n            } else {\n                constant = getConstant(right);\n                if (constant != null) {\n                    consumer.accept(left, constant);\n                }\n            }\n        }\n    }\n    \n    public static Variable getWrittenVariable(Node node) {\n        if (!(node instanceof Expression))\n            return null;\n        Expression expr = (Expression) node;\n        if (expr.getOperand() instanceof Variable && (expr\n                .getCode() == AstCode.Store || expr.getCode() == AstCode.Inc))\n            return (Variable) expr.getOperand();\n        if (expr.getCode() == AstCode.PreIncrement || expr.getCode() == AstCode.PostIncrement) {\n            Object var = expr.getArguments().get(0).getOperand();\n            if(var instanceof Variable)\n                return (Variable) var;\n        }\n        return null;\n    }\n    \n    public static boolean isWriteTo(Node node, Set<Variable> vars) {\n        Variable var = getWrittenVariable(node);\n        return var != null && vars.contains(var);\n    }\n\n    public static boolean isComparison(Node node) {\n        if (!(node instanceof Expression) || ((Expression)node).getArguments().size() != 2)\n            return false;\n        switch (((Expression) node).getCode()) {\n        case CmpEq:\n        case CmpGe:\n        case CmpGt:\n        case CmpLe:\n        case CmpLt:\n        case CmpNe:\n            return true;\n        default:\n            return false;\n        }\n    }\n\n    public static boolean isBinaryMath(Node node) {\n        if (!(node instanceof Expression) || ((Expression)node).getArguments().size() != 2)\n            return false;\n        switch (((Expression) node).getCode()) {\n        case Add:\n        case Sub:\n        case Mul:\n        case Div:\n        case Rem:\n        case Shl:\n        case Shr:\n        case UShr:\n        case And:\n        case Or:\n        case Xor:\n            return true;\n        default:\n            return false;\n        }\n    }\n\n    public static boolean isBoxing(Node node) {\n        if (!isOp(node, AstCode.InvokeStatic))\n            return false;\n        MethodReference ref = (MethodReference) ((Expression) node).getOperand();\n        if (!ref.getName().equals(\"valueOf\"))\n            return false;\n        TypeReference type = ref.getDeclaringType();\n        if (type.getInternalName().equals(\"java/lang/Double\") && ref.getSignature().equals(\"(D)Ljava/lang/Double;\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Integer\") && ref.getSignature().equals(\"(I)Ljava/lang/Integer;\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Long\") && ref.getSignature().equals(\"(J)Ljava/lang/Long;\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Boolean\") && ref.getSignature().equals(\"(Z)Ljava/lang/Boolean;\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Short\") && ref.getSignature().equals(\"(S)Ljava/lang/Short;\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Character\") && ref.getSignature().equals(\n            \"(C)Ljava/lang/Character;\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Float\") && ref.getSignature().equals(\"(F)Ljava/lang/Float;\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Byte\") && ref.getSignature().equals(\"(B)Ljava/lang/Byte;\"))\n            return true;\n        return false;\n    }\n\n    public static boolean isUnboxing(Node node) {\n        if (!isOp(node, AstCode.InvokeVirtual))\n            return false;\n        MethodReference ref = (MethodReference) ((Expression) node).getOperand();\n        TypeReference type = ref.getDeclaringType();\n        if (type.getInternalName().equals(\"java/lang/Double\") && ref.getName().equals(\"doubleValue\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Integer\") && ref.getName().equals(\"intValue\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Long\") && ref.getName().equals(\"longValue\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Boolean\") && ref.getName().equals(\"booleanValue\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Short\") && ref.getName().equals(\"shortValue\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Character\") && ref.getName().equals(\"charValue\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Float\") && ref.getName().equals(\"floatValue\"))\n            return true;\n        if (type.getInternalName().equals(\"java/lang/Byte\") && ref.getName().equals(\"byteValue\"))\n            return true;\n        return false;\n    }\n    \n    public static boolean isEquivalent(Node expr1, Node expr2) {\n        if (expr1 == expr2)\n            return true;\n        if (expr1 == null)\n            return expr2 == null;\n        if (expr1 instanceof Expression && expr2 instanceof Expression)\n            return Equi.equiExpressions((Expression) expr1, (Expression) expr2) && \n                    Inf.PURITY.get((Expression)expr1).atLeast(Purity.HEAP_DEP);\n        return false;\n    }\n\n    public static boolean isSideEffectFree(Node node) {\n        if (node == null)\n            return true;\n        if (!(node instanceof Expression)) {\n            for(Node child : getChildren(node)) {\n                if(!isSideEffectFree(child)) {\n                    return false;\n                }\n            }\n            return true;\n        }\n        return Inf.PURITY.isSideEffectFree((Expression) node);\n    }\n\n    public static boolean isPure(Node node) {\n        if (node == null)\n            return true;\n        if (!(node instanceof Expression))\n            return false;\n        return Inf.PURITY.isPure((Expression) node);\n    }\n\n    public static boolean isSynchorizedBlock(Node node) {\n        if (!(node instanceof TryCatchBlock)) {\n            return false;\n        }\n        TryCatchBlock tcb = (TryCatchBlock) node;\n        return getSyncObject(tcb) != null;\n    }\n\n    public static Expression getSyncObject(TryCatchBlock tcb) {\n        Block finallyBlock = tcb.getFinallyBlock();\n        if (finallyBlock == null)\n            return null;\n        List<Node> list = finallyBlock.getBody();\n        if (list.size() != 1)\n            return null;\n        Node n = list.get(0);\n        if (!Nodes.isOp(n, AstCode.MonitorExit))\n            return null;\n        Expression e = ((Expression)n).getArguments().get(0);\n        if(e.getOperand() instanceof Variable && ((Variable)e.getOperand()).isGenerated()) {\n            return ValuesFlow.getSource(e);\n        }\n        return e;\n    }\n\n    public static boolean isCompoundAssignment(Node node) {\n        if (!(node instanceof Expression))\n            return false;\n        Expression store = (Expression) node;\n        switch (store.getCode()) {\n        case Store: {\n            Expression expr = dropNarrowing(store.getArguments().get(0));\n            if (!isBinaryMath(expr))\n                return false;\n            Expression load = expr.getArguments().get(0);\n            return load.getCode() == AstCode.Load && Objects.equals(load.getOperand(), store.getOperand());\n        }\n        case PutField: {\n            Expression expr = dropNarrowing(store.getArguments().get(1));\n            if (!isBinaryMath(expr))\n                return false;\n            Expression load = expr.getArguments().get(0);\n            return load.getCode() == AstCode.GetField\n                && ((FieldReference) load.getOperand()).isEquivalentTo((MemberReference) store.getOperand())\n                && Equi.equiExpressions(store.getArguments().get(0), load.getArguments().get(0));\n        }\n        case PutStatic: {\n            Expression expr = dropNarrowing(store.getArguments().get(0));\n            if (!isBinaryMath(expr))\n                return false;\n            Expression load = expr.getArguments().get(0);\n            return load.getCode() == AstCode.GetStatic\n                && ((FieldReference) load.getOperand()).isEquivalentTo((MemberReference) store.getOperand());\n        }\n        default:\n            return false;\n        }\n    }\n\n    private static Expression dropNarrowing(Expression expression) {\n        switch(expression.getCode()) {\n        case I2B:\n        case I2C:\n        case I2S:\n        case L2I:\n        case D2F:\n        case D2I:\n            return expression.getArguments().get(0);\n        default:\n            return expression;\n        }\n    }\n\n    public static Node find(Node node, Predicate<Node> predicate) {\n        if (predicate.test(node))\n            return node;\n        for (Node child : getChildren(node)) {\n            Node result = find(child, predicate);\n            if (result != null)\n                return result;\n        }\n        return null;\n    }\n    \n    @SuppressWarnings(\"unchecked\")\n    public static Iterable<Node> getChildren(Node node) {\n        if(node instanceof Expression) {\n            Expression expr = (Expression)node;\n            Object operand = expr.getOperand();\n            if(operand instanceof Lambda) {\n                return Iterables.concat(expr.getArguments(), Collections.singleton((Node)operand));\n            }\n            return (Iterable<Node>)(Iterable<?>)expr.getArguments();\n        }\n        if(node instanceof Block) {\n            Block block = (Block) node;\n            Expression entryGoto = block.getEntryGoto();\n            if(entryGoto != null) {\n                return Iterables.concat(Collections.singleton(entryGoto), block.getBody());\n            }\n            return block.getBody();\n        }\n        \n        return node.getChildren();\n    }\n\n    public static boolean isEmptyOrBreak(Block block) {\n        List<Node> body = block.getBody();\n        if (body.isEmpty())\n            return true;\n        if (body.size() > 1)\n            return false;\n        Node node = body.get(0);\n        if (isOp(node, AstCode.LoopOrSwitchBreak) || isOp(node, AstCode.Return) || isOp(node, AstCode.LoopContinue)) {\n            Expression expr = (Expression) node;\n            if (expr.getOperand() == null && expr.getArguments().size() == 0)\n                return true;\n        }\n        return false;\n    }\n\n    public static String getOperation(AstCode code) {\n        switch (code) {\n        case CmpEq:\n            return \"==\";\n        case CmpNe:\n            return \"!=\";\n        case CmpLe:\n            return \"<=\";\n        case CmpLt:\n            return \"<\";\n        case CmpGe:\n            return \">=\";\n        case CmpGt:\n            return \">\";\n        case And:\n            return \"&\";\n        case Or:\n            return \"|\";\n        case Xor:\n            return \"^\";\n        case Sub:\n            return \"-\";\n        case Div:\n            return \"/\";\n        case Rem:\n            return \"%\";\n        case LogicalAnd:\n            return \"&&\";\n        case LogicalOr:\n            return \"||\";\n        case Shl:\n            return \"<<\";\n        case Shr:\n            return \">>\";\n        case UShr:\n            return \">>>\";\n        default:\n            return code.getName();\n        }\n    }\n\n    public static MethodDefinition getLambdaMethod(Lambda l) {\n        Object arg = l.getCallSite().getBootstrapArguments().get(1);\n        if (arg instanceof MethodHandle) {\n            MethodDefinition lm = ((MethodHandle) arg).getMethod().resolve();\n            if (lm != null)\n                return lm;\n        }\n        throw new InternalError(\"Unable to determine original method for lambda \" + l);\n    }\n\n    public static int estimateCodeSize(Node node) {\n        return node.getChildrenAndSelfRecursive().size();\n    }\n\n    public static MethodHandle getMethodHandle(DynamicCallSite dcs) {\n        MethodHandle mh = dcs.getBootstrapMethodHandle();\n        if (mh.getMethod().getDeclaringType().getInternalName().equals(\"java/lang/invoke/LambdaMetafactory\")) {\n            List<Object> args = dcs.getBootstrapArguments();\n            if (args.size() > 1 && args.get(1) instanceof MethodHandle) {\n                MethodHandle actualHandle = (MethodHandle) args.get(1);\n                return actualHandle;\n            }\n        }\n        return null;\n    }\n\n    public static boolean isThrow(Node node) {\n        if (Nodes.isOp(node, AstCode.AThrow))\n            return true;\n        if (node instanceof Block) {\n            List<Node> list = ((Block) node).getBody();\n            if (list.size() == 1)\n                return isThrow(list.get(0));\n        }\n        return false;\n    }\n    \n    public static boolean isToFloatingPointConversion(Node node) {\n        if(!(node instanceof Expression))\n            return false;\n        Expression expr = (Expression) node;\n        return expr.getCode() == AstCode.I2F || expr.getCode() == AstCode.I2D || expr.getCode() == AstCode.L2F\n                || expr.getCode() == AstCode.L2D;\n    }\n    \n    public static String facts(Node node) {\n        if(node instanceof Expression)\n            return Annotators.facts((Expression) node);\n        return \"{}\";\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Types.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport com.strobel.assembler.ir.attributes.SourceAttribute;\nimport com.strobel.assembler.ir.attributes.SourceFileAttribute;\nimport com.strobel.assembler.metadata.BuiltinTypes;\nimport com.strobel.assembler.metadata.MetadataSystem;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Variable;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class Types {\n    private static final Set<String> SIDE_EFFECT_FREE_TYPES = new HashSet<>(Arrays.asList(\"java/lang/Integer\",\n        \"java/lang/Long\", \"java/lang/Short\", \"java/lang/Double\", \"java/lang/Byte\", \"java/lang/Character\",\n        \"java/lang/Boolean\", \"java/lang/Float\", \"java/lang/Math\"));\n\n    private static final Set<String> BOXED_TYPES = new HashSet<>(Arrays.asList(\"java/lang/Integer\", \"java/lang/Long\",\n        \"java/lang/Short\", \"java/lang/Double\", \"java/lang/Byte\", \"java/lang/Character\", \"java/lang/Boolean\",\n        \"java/lang/Float\"));\n\n    private static final Set<String> MUTABLE_TYPES = new HashSet<>(Arrays.asList(\"java/util/Hashtable\",\n        \"java/util/Vector\", \"java/util/Date\", \"java/sql/Date\", \"java/sql/Timestamp\", \"java/awt/Point\",\n        \"java/awt/Dimension\", \"java/awt/Rectangle\"));\n    \n    private static MetadataSystem ms = MetadataSystem.instance();\n\n    public static synchronized TypeDefinition lookupJdkType(String internalName) {\n        TypeReference tr = ms.lookupType(internalName);\n        if (tr == null) {\n            throw new InternalError(\"Unable to lookup type \" + internalName);\n        }\n        TypeDefinition td = tr.resolve();\n        if (td == null) {\n            throw new InternalError(\"Unable to resolve type \" + internalName);\n        }\n        return td;\n    }\n\n\n    public static List<TypeReference> getBaseTypes(TypeReference input) {\n        List<TypeReference> result = new ArrayList<>();\n        while (true) {\n            result.add(input);\n            TypeDefinition td = input.resolve();\n            if (td == null)\n                break;\n            input = td.getBaseType();\n            if (input == null)\n                break;\n        }\n        Collections.reverse(result);\n        return result;\n    }\n\n    public static boolean isInstance(TypeReference type, TypeReference wantedType) {\n        return isInstance(type, wantedType.getInternalName());\n    }\n\n    public static boolean isInstance(TypeReference type, String wantedType) {\n        if (type == null || type.isPrimitive())\n            return false;\n        if (wantedType.equals(\"java/lang/Object\"))\n            return true;\n        if (type.getInternalName().equals(wantedType))\n            return true;\n        if (type.isArray()) {\n            if(!wantedType.startsWith(\"[\"))\n                return false;\n            return isInstance(type.getElementType(), wantedType.substring(1));\n        }\n        TypeDefinition td = type.resolve();\n        if (td == null)\n            return false;\n        for (TypeReference iface : td.getExplicitInterfaces()) {\n            if (isInstance(iface, wantedType))\n                return true;\n        }\n        TypeReference bt = td.getBaseType();\n        if (bt == null)\n            return false;\n        return isInstance(bt, wantedType);\n    }\n\n    public static boolean isRandomClass(TypeReference type) {\n        String typeName = type.getInternalName();\n        return typeName.equals(\"java/util/Random\") || typeName.equals(\"java/security/SecureRandom\")\n            || typeName.equals(\"java/util/concurrent/ThreadLocalRandom\")\n            || typeName.equals(\"java/util/SplittableRandom\")\n            || typeName.startsWith(\"cern/jet/random/engine/\");\n    }\n\n    public static TypeReference getExpressionType(Expression expr) {\n        TypeReference exprType = expr.getInferredType();\n        if (expr.getOperand() instanceof Variable) {\n            Variable var = (Variable) expr.getOperand();\n            exprType = var.getType();\n            if (var.getOriginalParameter() != null)\n                exprType = var.getOriginalParameter().getParameterType();\n        }\n        return exprType;\n    }\n\n    /**\n     * @param type\n     * @return true if all methods of given type are known not to produce\n     *         side-effects\n     */\n    public static boolean isSideEffectFreeType(TypeReference type) {\n        return SIDE_EFFECT_FREE_TYPES.contains(type.getInternalName()) || type.getInternalName().startsWith(\"java/time/\");\n    }\n\n    public static boolean samePackage(String internalName1, String internalName2) {\n        int pos = internalName1.lastIndexOf('/');\n        if (pos == -1)\n            return internalName2.indexOf('/') == 1;\n        return internalName2.startsWith(internalName1.substring(0, pos + 1));\n    }\n\n    /**\n     * @param type\n     * @return true if type is known to be mutable\n     */\n    public static boolean isMutable(TypeReference type) {\n        if (type.isArray())\n            return true;\n        return MUTABLE_TYPES.contains(type.getInternalName());\n    }\n\n    /**\n     * @param type\n     * @return true if type is known to be immutable\n     */\n    public static boolean isImmutable(TypeReference type) {\n        if (type == null) \n            return false;\n        if(BOXED_TYPES.contains(type.getInternalName()) || isString(type))\n            return true;\n        return false;\n    }\n    \n    public static boolean isBoxed(TypeReference type) {\n        return BOXED_TYPES.contains(type.getInternalName());\n    }\n    \n    public static boolean isObject(TypeReference type) {\n        return type.getInternalName().equals(\"java/lang/Object\");\n    }\n\n    public static boolean isCollection(TypeReference type) {\n        return isInstance(type, \"java/util/Collection\");\n    }\n    \n    public static boolean isStream(TypeReference type) {\n        return isInstance(type, \"java/util/stream/Stream\");\n    }\n    \n    public static boolean isString(TypeReference type) {\n        return type.getInternalName().equals(\"java/lang/String\");\n    }\n\n    public static boolean isBaseStream(TypeReference type) {\n        return isInstance(type, \"java/util/stream/BaseStream\");\n    }\n    \n    public static boolean is(TypeReference type, Class<?> clazz) {\n        return type != null && type.getFullName().equals(clazz.getName());\n    }\n\n    public static boolean is(TypeReference type, String internalName) {\n        return type != null && type.getInternalName().equals(internalName);\n    }\n    \n    /**\n     * @param type type to check\n     * @return true if all superclasses and superinterfaces could be loaded\n     */\n    public static boolean hasCompleteHierarchy(TypeDefinition type) {\n        if(type == null)\n            return false;\n        if(type.isArray())\n            return hasCompleteHierarchy(type.getElementType().resolve());\n        TypeReference base = type.getBaseType();\n        if(base != null && !hasCompleteHierarchy(base.resolve()))\n            return false;\n        for(TypeReference tr : type.getExplicitInterfaces()) {\n            if(!hasCompleteHierarchy(tr.resolve()))\n                return false;\n        }\n        return true;\n    }\n\n    public static TypeReference mergeTypes(TypeReference t1, TypeReference t2) {\n        if (t1 == null || t2 == null)\n            return null;\n        if (t1 == BuiltinTypes.Null)\n            return t2;\n        if (t2 == BuiltinTypes.Null)\n            return t1;\n        if (t1.isEquivalentTo(t2))\n            return t1;\n        if(t1.isArray() ^ t2.isArray())\n            return null;\n        if(t1.isArray()) {\n            TypeReference merged = mergeTypes(t1.getElementType(), t2.getElementType());\n            return merged == null ? null : merged.makeArrayType();\n        }\n        List<TypeReference> chain1 = getBaseTypes(t1);\n        List<TypeReference> chain2 = getBaseTypes(t2);\n        for (int i = Math.min(chain1.size(), chain2.size()) - 1; i >= 0; i--) {\n            if (chain1.get(i).equals(chain2.get(i)))\n                return chain1.get(i);\n        }\n        return null;\n    }\n\n    public static String getSourceFile(TypeDefinition type) {\n        for(SourceAttribute sa : type.getSourceAttributes()) {\n            if(sa instanceof SourceFileAttribute) {\n                return ((SourceFileAttribute)sa).getSourceFile();\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Variables.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.lang.reflect.Field;\nimport java.security.AccessController;\nimport java.security.PrivilegedAction;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.VariableDefinition;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class Variables {\n    static final Field variableMethodDefinitionField;\n\n    static {\n        variableMethodDefinitionField = AccessController.doPrivileged((PrivilegedAction<Field>) () -> {\n            try {\n                Field f = VariableDefinition.class.getDeclaredField(\"_declaringMethod\");\n                f.setAccessible(true);\n                return f;\n            } catch (NoSuchFieldException | SecurityException e) {\n                throw new InternalError(e);\n            }\n        });\n    }\n\n    public static MethodDefinition getMethodDefinition(VariableDefinition vd) {\n        try {\n            return (MethodDefinition) variableMethodDefinitionField.get(vd);\n        } catch (IllegalArgumentException | IllegalAccessException e) {\n            throw new InternalError(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/Xml.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\n/**\n * @author lan\n *\n */\npublic class Xml {\n    \n    public static Stream<Element> elements(Element parent) {\n        NodeList nodes = parent.getChildNodes();\n        return IntStream.range(0, nodes.getLength()).mapToObj(nodes::item).filter(Element.class::isInstance)\n                .map(Element.class::cast);\n    }\n\n    public static Element getChild(Element element, String tagName) {\n        Node node = element.getFirstChild();\n        while(node != null) {\n            if(node instanceof Element && ((Element)node).getTagName().equals(tagName)) {\n                return (Element) node;\n            }\n            node = node.getNextSibling();\n        }\n        return null;\n    }\n\n    public static String getText(Element element, String tagName) {\n        Element child = getChild(element, tagName);\n        return child == null ? \"\" : child.getTextContent();\n    }\n    \n    public static String getAttribute(Element element, String attribute) {\n        if(element.hasAttribute(attribute))\n            return element.getAttribute(attribute);\n        return null;\n    }\n    \n    public static int getIntAttribute(Element element, String attribute, int defaultValue) {\n        String str = getAttribute(element, attribute);\n        if(str == null)\n            return defaultValue;\n        try {\n            return Integer.parseInt(str);\n        } catch (NumberFormatException e) {\n            return defaultValue;\n        }\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/util/YesNoMaybe.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\n/**\n * @author shustkost\n *\n */\npublic enum YesNoMaybe {\n    YES, MAYBE, NO;\n    \n    public boolean yes() {\n        return this == YES;\n    }\n    \n    public boolean no() {\n        return this == NO;\n    }\n\n    public static YesNoMaybe of(boolean b) {\n        return b ? YES : NO;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/Formatter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning;\n\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.core.StringUtilities;\n\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\nimport one.util.huntbugs.warning.WarningAnnotation.TypeInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class Formatter {\n    public static final String FORMAT_PLAIN = \"plain\";\n    public static final String FORMAT_TYPE_PLAIN = \"type.plain\";\n    public static final String FORMAT_HTML = \"html\";\n    public static final String FORMAT_TYPE_HTML = \"type.html\";\n    public static final String FORMAT_NAME = \"name\";\n    public static final String FORMAT_HEX = \"hex\";\n    public static final String FORMAT_DEC = \"dec\";\n    public static final String FORMAT_CONST = \"const\";\n\n    private final Messages msgs;\n\n    public Formatter() {\n        this(Messages.load());\n    }\n\n    public Formatter(Messages msgs) {\n        this.msgs = msgs;\n    }\n\n    public String getTitle(Warning warning) {\n        return msgs.getMessagesForType(warning.getType()).getTitle();\n    }\n\n    public String getDescription(Warning warning) {\n        return format(msgs.getMessagesForType(warning.getType()).getDescription(), warning, FORMAT_PLAIN);\n    }\n\n    public String getLongDescription(Warning warning) {\n        return format(msgs.getMessagesForType(warning.getType()).getLongDescription(), warning, FORMAT_HTML);\n    }\n\n    private String format(String description, Warning warning, String format) {\n        String[] fields = description.split(\"\\\\$\", -1);\n        if (fields.length == 1)\n            return description;\n        StringBuilder result = new StringBuilder(fields[0]);\n        for (int i = 1; i < fields.length; i++) {\n            if (i % 2 == 0) {\n                result.append(fields[i]);\n            } else {\n                String key = fields[i];\n                String f = format;\n                int pos = key.indexOf(':');\n                if (pos > 0) {\n                    f = key.substring(pos + 1);\n                    key = key.substring(0, pos);\n                }\n                WarningAnnotation<?> anno = warning.getAnnotation(key);\n                if (anno == null) {\n                    result.append('(').append(key).append(')');\n                } else {\n                    result.append(formatValue(anno.getValue(), f));\n                }\n            }\n        }\n        return result.toString();\n    }\n\n    public static String formatValue(Object value, String format) {\n        if (value instanceof MemberInfo) {\n            return formatMemberInfo((MemberInfo) value, format);\n        }\n        if (value instanceof TypeInfo) {\n            return formatTypeInfo((TypeInfo) value, format);\n        }\n        if (value instanceof Double) {\n            return formatDouble((Double) value);\n        }\n        if (value instanceof Float) {\n            return formatFloat((Float) value);\n        }\n        if (value instanceof Integer) {\n            return formatInteger((Integer) value, format);\n        }\n        if (value instanceof Long) {\n            return formatLong((Long) value, format);\n        }\n        if (value instanceof String) {\n            return formatString((String) value, format);\n        }\n        return String.valueOf(value);\n    }\n\n    private static String formatString(String value, String format) {\n        if (format.equals(\"const\")) {\n            return StringUtilities.escape(value, true);\n        }\n        return value;\n    }\n\n    private static String formatLong(long value, String format) {\n        if (format.equals(FORMAT_HEX)) {\n            return \"0x\" + Long.toHexString(value);\n        }\n        if (format.equals(FORMAT_DEC)) {\n            return Long.toString(value);\n        }\n        if (value == Long.MIN_VALUE) {\n            return \"Long.MIN_VALUE\";\n        }\n        if (value == Long.MAX_VALUE) {\n            return \"Long.MAX_VALUE\";\n        }\n        if (value == Integer.MIN_VALUE) {\n            return \"Integer.MIN_VALUE\";\n        }\n        if (value == Integer.MAX_VALUE) {\n            return \"Integer.MAX_VALUE\";\n        }\n        if (Math.abs(value) >= 0xFF\n            && (Long.bitCount(value)+1 < Long.toString(value).length() || 66 - Long.numberOfLeadingZeros(value)\n                - Long.bitCount(value) < Long.toString(value).length())) {\n            return \"0x\" + Long.toHexString(value);\n        }\n        return Long.toString(value);\n    }\n\n    private static String formatInteger(int value, String format) {\n        if (format.equals(FORMAT_HEX)) {\n            return \"0x\" + Integer.toHexString(value);\n        }\n        if (format.equals(FORMAT_DEC)) {\n            return Integer.toString(value);\n        }\n        if (value == Integer.MIN_VALUE) {\n            return \"Integer.MIN_VALUE\";\n        }\n        if (value == Integer.MAX_VALUE) {\n            return \"Integer.MAX_VALUE\";\n        }\n        return Integer.toString(value);\n    }\n\n    private static String formatFloat(float val) {\n        if (Float.isNaN(val))\n            return \"Float.NaN\";\n        if (val == Float.POSITIVE_INFINITY)\n            return \"Float.POSITIVE_INFINITY\";\n        if (val == Float.NEGATIVE_INFINITY)\n            return \"Float.NEGATIVE_INFINITY\";\n        if (val == Float.MIN_VALUE)\n            return \"Float.MIN_VALUE\";\n        if (val == Float.MAX_VALUE)\n            return \"Float.MAX_VALUE\";\n        if (val == -Float.MIN_VALUE)\n            return \"-Float.MIN_VALUE\";\n        if (val == -Float.MAX_VALUE)\n            return \"-Float.MAX_VALUE\";\n        return Float.toString(val);\n    }\n\n    private static String formatDouble(double val) {\n        if (Double.isNaN(val))\n            return \"Double.NaN\";\n        if (val == Double.POSITIVE_INFINITY)\n            return \"Double.POSITIVE_INFINITY\";\n        if (val == Double.NEGATIVE_INFINITY)\n            return \"Double.NEGATIVE_INFINITY\";\n        if (val == Double.MIN_VALUE)\n            return \"Double.MIN_VALUE\";\n        if (val == Double.MAX_VALUE)\n            return \"Double.MAX_VALUE\";\n        if (val == -Double.MIN_VALUE)\n            return \"-Double.MIN_VALUE\";\n        if (val == -Double.MAX_VALUE)\n            return \"-Double.MAX_VALUE\";\n        return Double.toString(val);\n    }\n\n    private static String formatTypeInfo(TypeInfo ti, String format) {\n        String simpleName = ti.getSimpleName();\n        String result = simpleName;\n        if (format.equals(FORMAT_HTML))\n            return \"<code class=\\\"Member\\\" title=\\\"\" + ti + \"\\\">\" + result + \"</code>\";\n        return result;\n    }\n\n    private static String formatMemberInfo(MemberInfo mi, String format) {\n        if (format.equals(FORMAT_NAME))\n            return mi.getName();\n        if(format.startsWith(\"type.\"))\n            return formatTypeInfo(mi.getType(), format.substring(\"type.\".length()));\n        if(format.startsWith(\"return_type.\"))\n            return formatTypeInfo(mi.getReturnType(), format.substring(\"return_type.\".length()));\n        String type = mi.getTypeName();\n        int pos = type.lastIndexOf('/');\n        if (pos > -1)\n            type = type.substring(pos + 1).replace('$', '.');\n        String result;\n        if (mi.isMethod()) {\n            if (mi.getName().equals(\"<init>\"))\n                result = \"new \" + type + \"()\";\n            else if (mi.getName().equals(\"<clinit>\"))\n                result = type + \" static {}\";\n            else\n                result = type + \".\" + mi.getName() + \"()\";\n        } else {\n            result = type + \".\" + mi.getName();\n        }\n        if (format.equals(FORMAT_HTML))\n            return \"<code class=\\\"Member\\\" title=\\\"\" + mi + \"\\\">\" + result + \"</code>\";\n        return result;\n    }\n\n    public static String formatConstant(Object constant) {\n        if(constant instanceof String) {\n            return StringUtilities.escape((String)constant, true);\n        }\n        if(constant instanceof TypeReference) {\n            return ((TypeReference)constant).getSimpleName()+\".class\";\n        }\n        if(constant instanceof Number) {\n            return formatValue(constant, FORMAT_PLAIN);\n        }\n        return String.valueOf(constant);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/Messages.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UncheckedIOException;\nimport java.net.URL;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport one.util.huntbugs.util.Xml;\n\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.xml.sax.SAXException;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class Messages {\n    private static final String MESSAGES_XML = \"huntbugs/messages.xml\";\n    \n    private final Map<String, Message> map;\n    \n    public static class Message {\n        private final String title, description, longDescription;\n\n        public Message(String title, String description, String longDescription) {\n            this.title = Objects.requireNonNull(title);\n            this.description = Objects.requireNonNull(description);\n            this.longDescription = Objects.requireNonNull(longDescription);\n        }\n\n        public String getTitle() {\n            return title;\n        }\n\n        public String getDescription() {\n            return description;\n        }\n\n        public String getLongDescription() {\n            return longDescription.isEmpty() ? description : longDescription;\n        }\n    }\n    \n    private Messages(Map<String, Message> map) {\n        this.map = map;\n    }\n    \n    public Message getMessagesForType(WarningType warningType) {\n        return getMessagesForType(warningType.getName());\n    }\n    \n    public Message getMessagesForType(String warningType) {\n        Message message = map.get(warningType);\n        if(message == null) {\n            return new Message(warningType, warningType+\" in $METHOD$\", warningType);\n        }\n        return message;\n    }\n    \n    public static Messages load() {\n        Map<String, Message> allMessages = new HashMap<>();\n\n        try {\n            // 3-rd party detectors could provide their own messages\n            Enumeration<URL> messageUrls = Messages.class.getClassLoader().getResources(MESSAGES_XML);\n            while (messageUrls.hasMoreElements()) {\n                URL messageUrl = messageUrls.nextElement();\n                allMessages.putAll(toMap(readMessages(messageUrl)));\n            }\n        } catch (IOException ex) {\n            throw new UncheckedIOException(ex);\n        }\n\n        return new Messages(allMessages);\n    }\n\n    private static Document readMessages(URL messageUrl) {\n        try (InputStream is = messageUrl.openStream()) {\n            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n            DocumentBuilder builder = factory.newDocumentBuilder();\n            return builder.parse(is);\n        } catch (IOException ex) {\n            throw new UncheckedIOException(ex);\n        } catch (ParserConfigurationException | SAXException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private static Map<String, Message> toMap(Document dom) {\n        Map<String, Message> map = new HashMap<>();\n        Element element = dom.getDocumentElement();\n        Element warnings = Xml.getChild(element, \"WarningList\");\n        if(warnings != null) {\n            Node node = warnings.getFirstChild();\n            while(node != null) {\n                if(node instanceof Element && ((Element)node).getTagName().equals(\"Warning\")) {\n                    Element warning = (Element) node;\n                    String type = warning.getAttribute(\"Type\");\n                    String title = Xml.getText(warning, \"Title\");\n                    String description = Xml.getText(warning, \"Description\");\n                    String longDescription = Xml.getText(warning, \"LongDescription\");\n                    if(map.containsKey(type)) {\n                        throw new IllegalStateException(\"Warning type \"+type+\" is declared twice\");\n                    }\n                    map.put(type, new Message(title, description, longDescription));\n                }\n                node = node.getNextSibling();\n            }\n        }\n        return map;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/Role.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning;\n\nimport java.util.Objects;\n\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.MethodReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.AstCode;\nimport com.strobel.decompiler.ast.Expression;\nimport com.strobel.decompiler.ast.Node;\n\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.util.ExpressionFormatter;\nimport one.util.huntbugs.util.Methods;\nimport one.util.huntbugs.util.Nodes;\nimport one.util.huntbugs.warning.WarningAnnotation.Location;\nimport one.util.huntbugs.warning.WarningAnnotation.MemberInfo;\nimport one.util.huntbugs.warning.WarningAnnotation.TypeInfo;\n\n/**\n * @author Tagir Valeev\n * \n *         Annotation roles\n */\npublic class Role<T> {\n    public enum Count {\n        ANY, ZERO_ONE, ONE;\n    }\n\n    private final String name;\n    private final Class<T> type;\n    private final Count count;\n\n    Role(String name, Class<T> type) {\n        this(name, type, Count.ANY);\n    }\n\n    Role(String name, Class<T> type, Count count) {\n        this.name = Objects.requireNonNull(name);\n        this.type = Objects.requireNonNull(type);\n        this.count = Objects.requireNonNull(count);\n    }\n\n    /**\n     * @return how many times such annotation could be used via single warning\n     */\n    public Count getCount() {\n        return count;\n    }\n\n    public Class<T> getType() {\n        return type;\n    }\n\n    @Override\n    public int hashCode() {\n        return name.hashCode() * 31 + type.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (!(obj instanceof Role))\n            return false;\n        Role<?> other = (Role<?>) obj;\n        return Objects.equals(name, other.name) && Objects.equals(type, other.type);\n    }\n\n    @Override\n    public String toString() {\n        return name;\n    }\n\n    public WarningAnnotation<T> create(T value) {\n        return new WarningAnnotation<>(this, value);\n    }\n\n    public static class MemberRole extends Role<MemberInfo> {\n        MemberRole(String name) {\n            super(name, MemberInfo.class);\n        }\n\n        MemberRole(String name, Count count) {\n            super(name, MemberInfo.class, count);\n        }\n\n        public WarningAnnotation<MemberInfo> create(MemberReference mr) {\n            return create(new MemberInfo(mr));\n        }\n\n        public WarningAnnotation<MemberInfo> create(String internalTypeName, String name, String signature) {\n            return create(new MemberInfo(internalTypeName, name, signature));\n        }\n        \n        public static MemberRole forName(String name) {\n            return new MemberRole(name);\n        }\n    }\n    \n    public static class NumberRole extends Role<Number> {\n        NumberRole(String name) {\n            super(name, Number.class);\n        }\n\n        NumberRole(String name, Count count) {\n            super(name, Number.class, count);\n        }\n        \n        public static NumberRole forName(String name) {\n            return new NumberRole(name);\n        }\n    }\n    \n    public static class StringRole extends Role<String> {\n        StringRole(String name) {\n            super(name, String.class);\n        }\n        \n        StringRole(String name, Count count) {\n            super(name, String.class, count);\n        }\n        \n        public WarningAnnotation<String> createFromConst(Object constant) {\n            return create(Formatter.formatConstant(constant));\n        }\n        \n        public static StringRole forName(String name) {\n            return new StringRole(name);\n        }\n    }\n    \n    public static class TypeRole extends Role<TypeInfo> {\n        TypeRole(String name) {\n            super(name, TypeInfo.class);\n        }\n\n        TypeRole(String name, Count count) {\n            super(name, TypeInfo.class, count);\n        }\n        \n        public WarningAnnotation<TypeInfo> create(TypeReference tr) {\n            return create(new TypeInfo(tr));\n        }\n        \n        public WarningAnnotation<TypeInfo> create(String internalTypeName) {\n            return create(new TypeInfo(internalTypeName));\n        }\n        \n        public static TypeRole forName(String name) {\n            return new TypeRole(name);\n        }\n    }\n    \n    public static class LocationRole extends Role<Location> {\n        LocationRole(String name) {\n            super(name, Location.class);\n        }\n        \n        LocationRole(String name, Count count) {\n            super(name, Location.class, count);\n        }\n        \n        public WarningAnnotation<Location> create(MethodContext mc, Node node) {\n            return create(mc.getLocation(node));\n        }\n        \n        public static LocationRole forName(String name) {\n            return new LocationRole(name);\n        }\n    }\n    \n    public static class OperationRole extends StringRole {\n        public OperationRole(String name, Count count) {\n            super(name, count);\n        }\n\n        public OperationRole(String name) {\n            super(name);\n        }\n        \n        public WarningAnnotation<String> create(AstCode code) {\n            return create(Nodes.getOperation(code));\n        }\n        \n        public WarningAnnotation<String> create(Expression expr) {\n            if(expr.getCode() == AstCode.InvokeVirtual && Methods.isEqualsMethod((MethodReference) expr.getOperand()))\n                return create(\"equals\");\n            return create(Nodes.getOperation(expr.getCode()));\n        }\n    }\n    \n    public static class ExpressionRole extends StringRole {\n        public ExpressionRole(String name, Count count) {\n            super(name, count);\n        }\n        \n        public ExpressionRole(String name) {\n            super(name);\n        }\n        \n        public WarningAnnotation<String> create(Expression expr) {\n            String str = ExpressionFormatter.formatExpression(expr);\n            return create(str.length() > 100 ? str.substring(0, 99)+\"...\" : str);\n        }\n        \n        public static ExpressionRole forName(String name) {\n            return new ExpressionRole(name);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/Roles.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning;\n\nimport one.util.huntbugs.warning.Role.Count;\nimport one.util.huntbugs.warning.Role.ExpressionRole;\nimport one.util.huntbugs.warning.Role.LocationRole;\nimport one.util.huntbugs.warning.Role.MemberRole;\nimport one.util.huntbugs.warning.Role.NumberRole;\nimport one.util.huntbugs.warning.Role.OperationRole;\nimport one.util.huntbugs.warning.Role.StringRole;\nimport one.util.huntbugs.warning.Role.TypeRole;\n\n/**\n * Predefined warning annotation roles.\n * \n * You may also create custom role using {@code forName} static methods in corresponding {@link Role} subclass,\n * e.g. {@code StringRole.forName(\"CUSTOM_STRING\")}.\n * \n * @author Tagir Valeev\n */\npublic final class Roles {\n    /**\n     * Class descriptor associated with the warning\n     */\n    public static final TypeRole TYPE = new TypeRole(\"TYPE\", Count.ONE);\n    /**\n     * Source file name (like \"Test.java\")\n     */\n    public static final StringRole FILE = new StringRole(\"FILE\", Count.ZERO_ONE);\n    /**\n     * Method associated with the warning (where warning appears)\n     */\n    public static final MemberRole METHOD = new MemberRole(\"METHOD\", Count.ZERO_ONE);\n    /**\n     * Field descriptor associated with the warning\n     */\n    public static final MemberRole FIELD = new MemberRole(\"FIELD\", Count.ZERO_ONE);\n    /**\n     * Source code location where warning appears\n     */\n    public static final LocationRole LOCATION = new LocationRole(\"LOCATION\", Count.ZERO_ONE);\n    /**\n     * Location where expression is used\n     */\n    public static final LocationRole USED_AT = new LocationRole(\"USED_AT\", Count.ZERO_ONE);\n    /**\n     * Another location where the same warning appears (within the same method)\n     */\n    public static final LocationRole ANOTHER_INSTANCE = new LocationRole(\"ANOTHER_INSTANCE\");\n    /**\n     * Location of the code which is never executed due to this warning\n     */\n    public static final LocationRole DEAD_CODE_LOCATION = LocationRole.forName(\"DEAD_CODE_LOCATION\");\n    /**\n     * Called method which is associated with the warning\n     */\n    public static final MemberRole CALLED_METHOD = new MemberRole(\"CALLED_METHOD\", Count.ZERO_ONE);\n    /**\n     * Method reference which is associated with the warning\n     */\n    public static final MemberRole METHOD_REFERENCE = new MemberRole(\"METHOD_REFERENCE\", Count.ZERO_ONE);\n    /**\n     * Class which should be used instead\n     */\n    public static final TypeRole REPLACEMENT_CLASS = new TypeRole(\"REPLACEMENT\", Count.ZERO_ONE);\n    /**\n     * Method which should be called instead\n     */\n    public static final MemberRole REPLACEMENT_METHOD = new MemberRole(\"REPLACEMENT\", Count.ZERO_ONE);\n    /**\n     * Super-class method associated with the warning\n     */\n    public static final MemberRole SUPER_METHOD = new MemberRole(\"SUPER_METHOD\", Count.ZERO_ONE);\n    /**\n     * Replacement expression or operator\n     */\n    public static final StringRole REPLACEMENT_STRING = new StringRole(\"REPLACEMENT\", Count.ZERO_ONE);\n    /**\n     * Local variable (or parameter) name associated with the warning\n     */\n    public static final StringRole VARIABLE = new StringRole(\"VARIABLE\");\n    /**\n     * Any string associated with the warning\n     */\n    public static final StringRole STRING = new StringRole(\"STRING\");\n    /**\n     * Any number associated with the warning\n     */\n    public static final NumberRole NUMBER = new NumberRole(\"NUMBER\");\n    /**\n     * Operation related to the warning like \"<\" or \"+\" or \"||\"\n     */\n    public static final OperationRole OPERATION = new OperationRole(\"OPERATION\");\n    /**\n     * Expression associated with warning\n     */\n    public static final ExpressionRole EXPRESSION = new ExpressionRole(\"EXPRESSION\");\n    /**\n     * Argument associated with warning\n     */\n    public static final ExpressionRole ARGUMENT = new ExpressionRole(\"ARGUMENT\");\n    /**\n     * Left argument associated with warning\n     */\n    public static final ExpressionRole LEFT_ARGUMENT = new ExpressionRole(\"LEFT_ARGUMENT\");\n    /**\n     * Right argument associated with warning\n     */\n    public static final ExpressionRole RIGHT_ARGUMENT = new ExpressionRole(\"RIGHT_ARGUMENT\");\n    /**\n     * Regular expression associated with the warning\n     */\n    public static final StringRole REGEXP = new StringRole(\"REGEXP\");\n    /**\n     * Minimal allowed value associated with warning \n     */\n    public static final NumberRole MIN_VALUE = new NumberRole(\"MIN_VALUE\", Count.ZERO_ONE);\n    /**\n     * Maximal allowed value associated with warning \n     */\n    public static final NumberRole MAX_VALUE = new NumberRole(\"MAX_VALUE\", Count.ZERO_ONE);\n    /**\n     * Expression target type associated with warning\n     */\n    public static final TypeRole TARGET_TYPE = new TypeRole(\"TARGET_TYPE\", Count.ZERO_ONE);\n    /**\n     * Field type associated with warning\n     */\n    public static final TypeRole FIELD_TYPE = new TypeRole(\"FIELD_TYPE\", Count.ZERO_ONE);\n    /**\n     * Array type associated with warning\n     */\n    public static final TypeRole ARRAY_TYPE = new TypeRole(\"ARRAY_TYPE\", Count.ZERO_ONE);\n    /**\n     * Value type associated with warning\n     */\n    public static final TypeRole VALUE_TYPE = new TypeRole(\"VALUE_TYPE\", Count.ZERO_ONE);\n    /**\n     * Type of Exception associated with the warning\n     */\n    public static final TypeRole EXCEPTION = new TypeRole(\"EXCEPTION\");\n    /**\n     * Interface type associated with the warning\n     */\n    public static final TypeRole INTERFACE = TypeRole.forName(\"INTERFACE\");\n    /**\n     * Superclass type associated with the warning\n     */\n    public static final TypeRole SUPERCLASS = TypeRole.forName(\"SUPERCLASS\");\n    /**\n     * Subclass type associated with the warning\n     */\n    public static final TypeRole SUBCLASS = TypeRole.forName(\"SUBCLASS\");\n\n    private Roles() {\n        \n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/Warning.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport one.util.huntbugs.warning.WarningAnnotation.TypeInfo;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class Warning {\n    public static final int MIN_SCORE = 0;\n    public static final int MAX_SCORE = 100;\n\n    private final WarningType type;\n    private final int priority;\n    private final List<WarningAnnotation<?>> annotations;\n    private final WarningStatus status;\n    \n    public Warning(WarningType type, int priority, List<WarningAnnotation<?>> annotations) {\n        this(type, priority, annotations, WarningStatus.DEFAULT);\n    }\n\n    public Warning(WarningType type, int priority, List<WarningAnnotation<?>> annotations, WarningStatus status) {\n        this.status = status;\n        this.type = type;\n        if(priority < 0) {\n            throw new IllegalArgumentException(\"Priority is negative: \"+priority+\" (warning: \"+type.getName()+\")\");\n        }\n        this.priority = priority;\n        this.annotations = new ArrayList<>(new LinkedHashSet<>(annotations));\n    }\n    \n    public String getClassName() {\n        TypeInfo ti = getAnnotation(Roles.TYPE);\n        return ti == null ? \"(Unknown)\" : ti.toString();\n    }\n    \n    public <T> T getAnnotation(Role<T> role) {\n        for(WarningAnnotation<?> anno : annotations) {\n            if(anno.getRole().equals(role))\n                return role.getType().cast(anno.getValue());\n        }\n        return null;\n    }\n    \n    public WarningAnnotation<?> getAnnotation(String name) {\n        for(WarningAnnotation<?> anno : annotations) {\n            if(anno.getRole().toString().equals(name))\n                return anno;\n        }\n        return null;\n    }\n    \n    public Stream<WarningAnnotation<?>> annotations() {\n        return annotations.stream();\n    }\n\n    public int getScore() {\n        return saturateScore(type.getMaxScore() - priority);\n    }\n\n    public WarningType getType() {\n        return type;\n    }\n    \n    public WarningStatus getStatus() {\n        return status;\n    }\n\n    public Warning withStatus(WarningStatus status) {\n        if(this.status == status) {\n            return this;\n        }\n        return new Warning(type, priority, annotations, status);\n    }\n\n    public static int saturateScore(int score) {\n        return score < MIN_SCORE ? MIN_SCORE : score > MAX_SCORE ? MAX_SCORE : score;\n    }\n\n    @Override\n    public String toString() {\n        return type.getCategory() + \"/\" + type.getName() + \" (\" + getScore() + \")\\n\"\n            + annotations.stream().map(wa -> \"\\t\" + wa + \"\\n\").collect(Collectors.joining());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/WarningAnnotation.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning;\n\nimport java.util.Objects;\n\nimport com.strobel.assembler.metadata.MemberReference;\nimport com.strobel.assembler.metadata.TypeReference;\nimport com.strobel.decompiler.ast.Variable;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class WarningAnnotation<T> {\n    private final Role<T> role;\n    private final T value;\n\n    WarningAnnotation(Role<T> role, T value) {\n        super();\n        this.role = Objects.requireNonNull(role);\n        this.value = Objects.requireNonNull(value);\n    }\n\n    public Role<T> getRole() {\n        return role;\n    }\n\n    public T getValue() {\n        return value;\n    }\n\n    @Override\n    public int hashCode() {\n        return role.hashCode() * 31 + ((value == null) ? 0 : value.hashCode());\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null || getClass() != obj.getClass())\n            return false;\n        WarningAnnotation<?> other = (WarningAnnotation<?>) obj;\n        return Objects.equals(role, other.role) && Objects.equals(value, other.value);\n    }\n\n    public static class TypeInfo {\n        private final String typeName;\n\n        public TypeInfo(String typeName) {\n            this.typeName = Objects.requireNonNull(typeName);\n        }\n\n        public TypeInfo(TypeReference ref) {\n            this.typeName = ref.getInternalName();\n        }\n\n        public String getTypeName() {\n            return typeName;\n        }\n        \n        public String getJavaName() {\n            return typeName.replace('/', '.');\n        }\n\n        public String getSimpleName() {\n            String type = typeName;\n            String suffix = \"\";\n            while (type.startsWith(\"[\")) {\n                type = type.substring(1);\n                suffix += \"[]\";\n            }\n            switch (type) {\n            case \"B\":\n                type = \"byte\";\n                break;\n            case \"C\":\n                type = \"char\";\n                break;\n            case \"J\":\n                type = \"long\";\n                break;\n            case \"I\":\n                type = \"int\";\n                break;\n            case \"S\":\n                type = \"short\";\n                break;\n            case \"Z\":\n                type = \"boolean\";\n                break;\n            case \"F\":\n                type = \"float\";\n                break;\n            case \"D\":\n                type = \"double\";\n                break;\n            }\n            int pos = type.lastIndexOf('/');\n            if (pos > -1)\n                type = type.substring(pos + 1).replace('$', '.');\n            return type + suffix;\n        }\n\n        @Override\n        public int hashCode() {\n            return typeName.hashCode();\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            return this == obj\n                || (obj != null && getClass() == obj.getClass() && typeName.equals(((TypeInfo) obj).typeName));\n        }\n\n        @Override\n        public String toString() {\n            String type = typeName.replaceAll(\"[/$]\", \".\");\n            while (type.startsWith(\"[\"))\n                type = type.substring(1) + \"[]\";\n            return type;\n        }\n    }\n\n    public static class MemberInfo {\n        private final TypeInfo type;\n        private final String name;\n        private final String signature;\n\n        public MemberInfo(String typeName, String name, String signature) {\n            this.type = new TypeInfo(Objects.requireNonNull(typeName));\n            this.name = Objects.requireNonNull(name);\n            this.signature = Objects.requireNonNull(signature);\n        }\n\n        public MemberInfo(MemberReference mr) {\n            this.type = new TypeInfo(mr.getDeclaringType().getInternalName());\n            this.name = mr.getName();\n            this.signature = mr.getErasedSignature();\n        }\n\n        public String getTypeName() {\n            return type.getTypeName();\n        }\n        \n        public TypeInfo getType() {\n            return type;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public String getSignature() {\n            return signature;\n        }\n\n        public boolean isMethod() {\n            return signature.startsWith(\"(\");\n        }\n\n        @Override\n        public String toString() {\n            if (isMethod()) {\n                if (name.equals(\"<init>\"))\n                    return \"new \" + type.getTypeName() + signature;\n                return type.getTypeName() + \".\" + name + signature;\n            }\n            return type.getTypeName() + \".\" + name + \":\" + signature;\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(name, type, signature);\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (obj == null || getClass() != obj.getClass())\n                return false;\n            MemberInfo other = (MemberInfo) obj;\n            return name.equals(other.name) && signature.equals(other.signature) && type.equals(other.type);\n        }\n\n        public TypeInfo getReturnType() {\n            if(!isMethod())\n                throw new IllegalStateException(\"Must be called for method\");\n            String returnType = signature.substring(signature.indexOf(')')+1);\n            if(returnType.startsWith(\"L\"))\n                returnType = returnType.substring(1, returnType.length()-1);\n            return new TypeInfo(returnType);\n        }\n    }\n\n    public static class Location {\n        final int offset;\n        final int sourceLine;\n\n        public Location(int offset, int sourceLine) {\n            this.offset = offset;\n            this.sourceLine = sourceLine;\n        }\n\n        public int getOffset() {\n            return offset;\n        }\n\n        public int getSourceLine() {\n            return sourceLine;\n        }\n\n        @Override\n        public String toString() {\n            if (sourceLine != -1)\n                return \"byteCode: \" + offset + \"; line: \" + sourceLine;\n            return \"byteCode: \" + offset;\n        }\n\n        @Override\n        public int hashCode() {\n            return offset * 31 + sourceLine;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (obj == null || getClass() != obj.getClass())\n                return false;\n            Location other = (Location) obj;\n            return offset == other.offset && sourceLine == other.sourceLine;\n        }\n    }\n\n    @Override\n    public String toString() {\n        return getRole() + \": \" + getValue();\n    }\n\n    public static WarningAnnotation<String> forVariable(Variable var) {\n        return Roles.VARIABLE.create(var.getName());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/WarningStatus.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning;\n\n/**\n * Warning status suitable to compare new build with old build\n * \n * @author Tagir Valeev\n */\npublic enum WarningStatus {\n    /**\n     * Default status: either comparison is not performed or bug appears both in new and old builds\n     */\n    DEFAULT,\n    \n    /**\n     * Newly-discovered warning\n     */\n    ADDED,\n    \n    /**\n     * Changed warning annotation (and probably priority)\n     */\n    CHANGED,\n    \n    /**\n     * Warning is unchanged, but its score became higher\n     */\n    SCORE_RAISED,\n    \n    /**\n     * Warning is unchanged, but its score became lower\n     */\n    SCORE_LOWERED,\n    \n    /**\n     * Fixed warning (does not appear in the new build)\n     */\n    FIXED\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/WarningType.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning;\n\nimport one.util.huntbugs.registry.anno.WarningDefinition;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class WarningType {\n    private final String category;\n    private final String name;\n    private final int maxScore;\n\n    public WarningType(String category, String name, int maxScore) {\n        this.category = category;\n        this.name = name;\n        this.maxScore = maxScore;\n    }\n    \n    public WarningType(WarningDefinition def) {\n        this(def.category(), def.name(), def.maxScore());\n    }\n\n    public String getCategory() {\n        return category;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public int getMaxScore() {\n        return maxScore;\n    }\n\n    @Override\n    public String toString() {\n        return \"WarningType [category=\" + category + \", name=\" + name + \", maxScore=\" + maxScore + \"]\";\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/rule/CategoryRule.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning.rule;\n\nimport one.util.huntbugs.warning.WarningType;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class CategoryRule implements Rule {\n    private final int adjustment;\n    private final String category;\n\n    public CategoryRule(String category, int adjustment) {\n        this.category = category;\n        this.adjustment = adjustment;\n    }\n\n    @Override\n    public WarningType adjust(WarningType wt) {\n        if(wt.getCategory().equalsIgnoreCase(category))\n            return new WarningType(wt.getCategory(), wt.getName(), wt.getMaxScore()+adjustment);\n        return wt;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/rule/CompositeRule.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning.rule;\n\nimport java.util.List;\n\nimport one.util.huntbugs.warning.WarningType;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class CompositeRule implements Rule {\n    private final List<Rule> rules;\n\n    public CompositeRule(List<Rule> rules) {\n        super();\n        this.rules = rules;\n    }\n\n    @Override\n    public WarningType adjust(WarningType wt) {\n        for(Rule rule : rules) {\n            wt = rule.adjust(wt);\n        }\n        return wt;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/rule/RegexRule.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning.rule;\n\nimport java.util.regex.Pattern;\n\nimport one.util.huntbugs.warning.WarningType;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class RegexRule implements Rule {\n    private final Pattern regex;\n    private final int adjustment;\n    \n    public RegexRule(String regex, int adjustment) {\n        this.regex = Pattern.compile(regex);\n        this.adjustment = adjustment;\n    }\n\n    @Override\n    public WarningType adjust(WarningType wt) {\n        if(regex.matcher(wt.getName()).matches())\n            return new WarningType(wt.getCategory(), wt.getName(), wt.getMaxScore()+adjustment);\n        return wt;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/main/java/one/util/huntbugs/warning/rule/Rule.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.warning.rule;\n\nimport one.util.huntbugs.warning.WarningType;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic interface Rule {\n    WarningType adjust(WarningType wt);\n    \n    public static final Rule NULL = new Rule() {\n        @Override\n        public WarningType adjust(WarningType wt) {\n            return wt;\n        }\n    };\n}\n"
  },
  {
    "path": "huntbugs/src/main/resources/huntbugs/messages.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<p:Messages\n  xmlns:p=\"https://raw.githubusercontent.com/amaembo/huntbugs/master/huntbugs/src/main/resources/huntbugs\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"https://raw.githubusercontent.com/amaembo/huntbugs/master/huntbugs/src/main/resources/huntbugs messages.xsd\">\n  <!-- Some of these descriptions are taken from FindBugs project: http://findbugs.sourceforge.net/ -->\n  <WarningList>\n    <Warning Type=\"RoughConstantValue\">\n      <Title>Rough value of known constant is used</Title>\n      <Description>Constant $NUMBER$ should be replaced with $REPLACEMENT$</Description>\n      <LongDescription><![CDATA[Analysis found that constant value <code>$NUMBER$</code> is used which is close to the <code>$REPLACEMENT$</code>. \n   If <code>$REPLACEMENT$</code> was actually meant, it's better to use it instead for better precision and code clarity.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"AbsoluteValueOfHashCode\">\n      <Title>Bad attempt to compute absolute value of signed 32-bit hashcode</Title>\n      <Description>Bad attempt to compute absolute value of signed 32-bit hashcode in $METHOD$</Description>\n      <LongDescription><![CDATA[This code generates a hashcode and then computes the absolute value of that hashcode.<br> \n    If the hashcode is <code>Integer.MIN_VALUE</code>, then the result will be negative as well (since <code>Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE</code>). \n    One out of 2<sup>32</sup> strings have a hashCode of <code>Integer.MIN_VALUE</code>, including \"polygenelubricants\", \"GydZG_\" and \"DESIGNING WORKHOUSES\".]]></LongDescription>\n    </Warning>\n    <Warning Type=\"AbsoluteValueOfRandomInt\">\n      <Title>Bad attempt to compute absolute value of signed random integer</Title>\n      <Description>Bad attempt to compute absolute value of signed random integer in $METHOD$</Description>\n      <LongDescription><![CDATA[This code generates a random signed integer and then computes the absolute value of that random integer.<br> \n   If the number returned by the random number generator is <code>Integer.MIN_VALUE</code>, then the result will be negative as well (since <code>Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE</code>). \n   (Same problem arised for long values as well).]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ArrayHashCode\">\n      <Title>Invocation of hashCode() on an array</Title>\n      <Description>Invocation of hashCode() on an array in $METHOD$</Description>\n      <LongDescription><![CDATA[This code invokes <code>hashCode()</code> on an array. Calling <code>hashCode()</code> on an array returns the same value as <code>System.identityHashCode</code>, and ingores the contents and length of the array. If you need a <code>hashCode</code> that depends on the contents of an array <code>a</code>, use <code>java.util.Arrays.hashCode(a)</code>. ]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ArrayToString\">\n      <Title>Invocation of toString() on an array</Title>\n      <Description>Invocation of toString() on an array in $METHOD$</Description>\n      <LongDescription><![CDATA[The code invokes <code>toString()</code> on an array or implicitly converts an array into String. \n    This will generate a fairly useless result such as <code>[B@16f0472</code>. \n    Consider using <code>java.util.Arrays.toString</code> to convert the array into a readable String that gives the contents of the array.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CharArrayToString\">\n      <Title>Invocation of toString() on a char[] array</Title>\n      <Description>Invocation of toString() on a char[] array in $METHOD$</Description>\n      <LongDescription><![CDATA[The code invokes <code>toString()</code> on a <code>char[]</code> array or implicitly converts an array into String. \n    This will generate a fairly useless result such as <code>[C@16f0472</code>. \n    Consider using <code>new String(charArray)</code> to get a String consisting of <code>charArray</code> symbols\n    or <code>Arrays.toString(charArray)</code> to get a readable array representation like <code>[a, b, c]</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StreamToString\">\n      <Title>Invocation of toString() on a Stream</Title>\n      <Description>Invocation of toString() on a Stream in $METHOD$</Description>\n      <LongDescription><![CDATA[The code invokes <code>toString()</code> on a Stream object. \n    This will generate a fairly useless result such as <code>java.util.stream.ReferencePipeline&#36;Head@16f0472</code>.\n    Probably it was intended to collect the stream like <code>collect(Collectors.joining())</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FloatCompareToNaN\">\n      <Title>Useless attempt to compare floating point number to NaN</Title>\n      <Description>Useless attempt to compare a $USED_TYPE$ number to NaN</Description>\n      <LongDescription><![CDATA[The code compares a number of $USED_TYPE$ type to <code>NaN</code>.\n    This comparison is useless as it always returns false. To check whether the number is NaN or not use $REPLACEMENT$.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"MaxMinMethodReferenceForComparator\">\n      <Title>Invalid method reference is used as comparator</Title>\n      <Description>A reference to $METHOD_REFERENCE$ is erroneously used as a comparator</Description>\n      <LongDescription><![CDATA[The code uses a reference to $METHOD_REFERENCE$ method for <code>Comparator</code> interface.\n   While this code compiles, $METHOD_REFERENCE$ does not actually fulfill a comparator contract, thus you might have incorrect result\n   or exception at runtime. Probably you wanted to use <code>Comparator.naturalOrder()</code> or <code>Comparator.reverseOrder()</code> instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SelfAssignmentLocalInsteadOfField\">\n      <Title>Local variable is assigned to itself, and there is a field with an identical name</Title>\n      <Description>Local variable $VARIABLE$ is assigned to itself while there is a field with an identical name.</Description>\n      <LongDescription><![CDATA[This code assigns a local variable to itself like <code>$VARIABLE$ = $VARIABLE$</code> while there's a \n   field with the same name in the class. Probably <code>this.$VARIABLE$ = $VARIABLE$</code> was meant instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SelfAssignmentLocal\">\n      <Title>Local variable is assigned to itself</Title>\n      <Description>Local variable $VARIABLE$ is assigned to itself in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code assigns a local variable to itself like <code>$VARIABLE$ = $VARIABLE$</code>. Probably something else was meant.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"VolatileIncrement\">\n      <Title>An increment to a volatile field isn't atomic</Title>\n      <Description>Non-atomic increment to a volatile field $FIELD$.</Description>\n      <LongDescription><![CDATA[This code increments a volatile field $FIELD$. Such operation is not atomic, so increments could be lost under contention.\n    Consider using <code>AtomicInteger</code> or <code>AtomicLong</code> instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"VolatileMath\">\n      <Title>Non-atomic math operation is performed on volatile field</Title>\n      <Description>Non-atomic operation is performed on volatile field $FIELD$.</Description>\n      <LongDescription><![CDATA[This code updates a volatile field $FIELD$. Such operation is not atomic, so updates could be lost under contention.\n    Consider using <code>AtomicInteger</code> or <code>AtomicLong</code> instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"VolatileArray\">\n      <Title>Volatile array field is declared</Title>\n      <Description>Field $FIELD$ is a volatile array.</Description>\n      <LongDescription><![CDATA[Field $FIELD$ is a volatile array. However, reads and writes to array elements are not volatile,\n      so cannot be safely updated from different threads. Consider using $REPLACEMENT$ instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"InvalidMinMax\">\n      <Title>Incorrect combination of max and min</Title>\n      <Description>Incorrect code: $OUTER_FUNC$($OUTER_NUMBER$, $INNER_FUNC$($INNER_NUMBER$, value)).</Description>\n      <LongDescription><![CDATA[This code attempts to limit the value using incorrect sequence of max and min operations: <code>$OUTER_FUNC$($OUTER_NUMBER$, $INNER_FUNC$($INNER_NUMBER$, value))</code>.\n    Such code will always produce the same result <code>$OUTER_NUMBER$</code> (or <code>NaN</code> if a value is NaN). It seems that <code>$INNER_FUNC$($OUTER_NUMBER$, $OUTER_FUNC$($INNER_NUMBER$, value))</code>\n    was assumed instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NonShortCircuitDangerous\">\n      <Title>Potentially dangerous use of non-short-circuit logic</Title>\n      <Description>Potentially dangerous use of non-short-circuit operation $OPERATION$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code uses non-short-circuit operation <code>$LEFT_ARGUMENT$ <b>$OPERATION$</b> $RIGHT_ARGUMENT$</code> rather than short-circuit equivalent <code>$LEFT_ARGUMENT$ <b>$REPLACEMENT$</b> $RIGHT_ARGUMENT$</code>. \n   In addition, it seem possible that, depending on the value of the left hand side, you might not want to evaluate the right hand side (because it would have side effects), could cause an exception or could be expensive.<br>\n   Non-short-circuit logic causes both sides of the expression to be evaluated even when the result can be inferred from knowing the left-hand side. This can be less efficient and can result in errors if the left-hand side guards cases when evaluating the right-hand side can generate an error. \n   ]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NonShortCircuit\">\n      <Title>Questionable use of non-short-circuit logic</Title>\n      <Description>Questionable use of non-short-circuit operation $OPERATION$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code uses non-short-circuit operation <code>$LEFT_ARGUMENT$ <b>$OPERATION$</b> $RIGHT_ARGUMENT$</code> rather than short-circuit equivalent <code>$LEFT_ARGUMENT$ <b>$REPLACEMENT$</b> $RIGHT_ARGUMENT$</code>.<br>\n   Non-short-circuit logic causes both sides of the expression to be evaluated even when the result can be inferred from knowing the left-hand side. This can be less efficient and can result in errors if the left-hand side guards cases when evaluating the right-hand side can generate an error. \n   ]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RandomNextIntViaNextDouble\">\n      <Title>The nextDouble() or Math.random() method is used instead of nextInt()</Title>\n      <Description>Random value obtained via $CALLED_METHOD$ and multiplication.</Description>\n      <LongDescription><![CDATA[An integer random value is generated by multiplying result of $CALLED_METHOD$ to a number and casting the result to int. It's more performant to use\n      dedicated method $REPLACEMENT$.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RandomDoubleToInt\">\n      <Title>Random value from 0 to 1 is coerced to the integer 0</Title>\n      <Description>Random value obtained via $CALLED_METHOD$ is coerced to the integer 0 in $METHOD$.</Description>\n      <LongDescription><![CDATA[A double random value is generated via $CALLED_METHOD$ and converted into integer. This method generates values from 0 to 1 (excluding), so the result of this conversion is always zero. \n      You probably want to multiply the random value by something else before coercing it to an integer, or use the $REPLACEMENT$ method. \n   ]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RandomUsedOnlyOnce\">\n      <Title>Random object created and used only once</Title>\n      <Description>A $RANDOM_TYPE$ object created and used only once in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates a $RANDOM_TYPE$ object, uses it to generate one random number, and then discards it. This produces mediocre quality random numbers and is inefficient. If possible, rewrite the code so that the $RANDOM_TYPE$ object is created once and saved, \n   and each time a new random number is required invoke a method on the existing $RANDOM_TYPE$ object to obtain it.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SelfAssignmentArrayElement\">\n      <Title>Array element is assigned to itself</Title>\n      <Description>Array element is assigned to itself in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code assigns an array element to itself, directly or via an intermediate variable. Probably something else was meant.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SelfAssignmentField\">\n      <Title>Field is assigned to itself</Title>\n      <Description>A field $FIELD$ is assigned to itself in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code assigns a field $FIELD$ to itself, directly or via an intermediate variable. Probably something else was meant.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BigDecimalConstructedFromInfiniteOrNaN\">\n      <Title>BigDecimal constructed from Infinity or NaN</Title>\n      <Description>A BigDecimal object is constructed from $NUMBER$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code constructs a <code>BigDecimal</code> from <code>$NUMBER$</code>. This is not supported: a <code>NumberFormatException</code> will be thrown at runtime.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BigDecimalConstructedFromDouble\">\n      <Title>BigDecimal is constructed from double</Title>\n      <Description>A BigDecimal object is constructed from $DOUBLE_NUMBER$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code uses <code>new BigDecimal($DOUBLE_NUMBER$)</code>. This method will create a <code>BigDecimal</code> object which corresponds to the exact decimal representation of\n   given double-precision number, which is <code>$BIGDECIMAL_NUMBER$</code>. You probably wanted to use <code>BigDecimal.valueOf($DOUBLE_NUMBER$)</code> which would produce actually $DOUBLE_NUMBER$.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CompareBitAndIncompatible\">\n      <Title>Incompatible bit mask for bitwise and</Title>\n      <Description>A (x &amp; $AND_OPERAND$) is compared to $COMPARED_TO$ which is always false.</Description>\n      <LongDescription><![CDATA[This code compares <code>(x &amp; $AND_OPERAND$)</code> with <code>$COMPARED_TO$</code>. Regardless of <code>x</code> value the result of expression is never equal to <code>$COMPARED_TO$</code>\n   due to incompatible bit mask.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CompareBitOrIncompatible\">\n      <Title>Incompatible bit mask for bitwise or</Title>\n      <Description>A (x | $OR_OPERAND$) is compared to $COMPARED_TO$ which is always false.</Description>\n      <LongDescription><![CDATA[This code compares <code>(x | $OR_OPERAND$)</code> with <code>$COMPARED_TO$</code>. Regardless of <code>x</code> value the result of expression is never equal to <code>$COMPARED_TO$</code>\n   due to incompatible bit mask.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"DoubleLongBitsToDoubleOnInt\">\n      <Title>Double.longBitsToDouble invoked on an int</Title>\n      <Description>Double.longBitsToDouble invoked on an int in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code invokes $CALLED_METHOD$ method, but a 32 bit int value is passed as an argument. This almostly certainly is not intended and is unlikely to give the intended result.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NonAtomicOperationOnConcurrentMap\">\n      <Title>Non-atomic sequence of operations on ConcurrentMap</Title>\n      <Description>$SECOND_METHOD$ is called after $FIRST_METHOD$ in non-atomical manner.</Description>\n      <LongDescription><![CDATA[This code invokes $SECOND_METHOD$ method to update the key in concurrent map after invoking $FIRST_METHOD$ to check the same key.\n   This sequence is not atomic, so it's possible that concurrent thread updates the same key meanwhile and several objects associated with the same key will be used concurrently.<br>\n   As a better alternative consider using <code>putIfAbsent</code> or <code>replace</code> methods.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RemOne\">\n      <Title>Integer remainder modulo 1</Title>\n      <Description>Integer remainder modulo one in $METHOD$.</Description>\n      <LongDescription><![CDATA[The <code>(x % 1)</code> expression is computed here which always returns zero. Probably <code>(x & 1)</code> or <code>(x % 2)</code> was meant.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ResultOfComparisonIsStaticallyKnownDeadCode\">\n      <Title>Result of comparison is statically known resulting in dead code</Title>\n      <Description>A comparison $LEFT_OPERAND$ $OPERATION$ $RIGHT_OPERAND$ is always $RESULT$ which results in dead code.</Description>\n      <LongDescription><![CDATA[This code performs a comparison <code>$EXPRESSION$</code> which can be statically evaluated to \n         <code>$LEFT_OPERAND$ $OPERATION$ $RIGHT_OPERAND$</code>, thus always $RESULT$.\n        As a result some branch or the next condition in short-circuiting chain will never be executed. Probably something else was meant or \n        this comparison along with the dead branch could be deleted.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ResultOfComparisonIsStaticallyKnown\">\n      <Title>Result of comparison is statically known</Title>\n      <Description>A comparison $LEFT_OPERAND$ $OPERATION$ $RIGHT_OPERAND$ is always $RESULT$.</Description>\n      <LongDescription><![CDATA[This code performs a comparison <code>$EXPRESSION$</code> which can be statically evaluated to \n         <code>$LEFT_OPERAND$ $OPERATION$ $RIGHT_OPERAND$</code>, thus always $RESULT$.\n        Probably something else was meant or this comparison could be deleted.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SameConditionsExcluding\">\n      <Title>The same condition is repeatedly checked resulting in dead code</Title>\n      <Description>The same condition is repeatedly checked resulting in dead code in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code checks the same condition <code>$EXPRESSION$</code> twice. As a result some branch is never executed. Probably there's logical error or typo in the code.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SameConditions\">\n      <Title>The same condition is repeatedly checked</Title>\n      <Description>The same condition is repeatedly checked in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code checks the same condition <code>$EXPRESSION$</code> twice. Probably there's logical error or typo in the code.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SelfComparison\">\n      <Title>The expression is compared with itself</Title>\n      <Description>The expression is compared with itself in $METHOD$.</Description>\n      <LongDescription><![CDATA[The expression is compared with itself like <code>(x $OPERATION$ x)</code> which does not make sense. Probably there's logical error or typo in the code.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SelfEquals\">\n      <Title>The equals() method is called for the same expression</Title>\n      <Description>The equals() method is called for the same expression in $METHOD$.</Description>\n      <LongDescription><![CDATA[The expression is compared with itself like <code>x.equals(x)</code> which does not make sense. Probably there's logical error or typo in the code.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SelfComputation\">\n      <Title>The operand is repeating in computation making it nonsensical</Title>\n      <Description>The operation like x $OPERATION$ x is performed in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code performs an operation like <code>(x $OPERATION$ x)</code>. The result of this operation is \n   always the same regardless the value of <code>x</code> (not considering the NaNs). Probably there's logical error or typo in the code.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnnecessaryInstanceOf\">\n      <Title>The instanceof check is unnecessary</Title>\n      <Description>Unnecessary instanceof $TARGET_TYPE$ is performed in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code performs a check like <code>$EXPRESSION$ instanceof $TARGET_TYPE:plain$</code>. However it's statically known that\n      this check is always true as the following type constraint holds:<br>\n      <br><code>$EXPRESSION$ :- $ETYPE$</code><br><br>\n      Probably something else was meant or this check could be replaced with null check or removed at all.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ImpossibleInstanceOf\">\n      <Title>The instanceof check is always false</Title>\n      <Description>Impossible $EXPRESSION$ instanceof $TARGET_TYPE$ is performed in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code performs a check like <code>$EXPRESSION$ instanceof $TARGET_TYPE:plain$</code>. However it's statically known that\n      this check is always false as the following type constraint holds:<br>\n      <br><code>$EXPRESSION$ :- $ETYPE$</code><br><br>\n      Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ImpossibleCast\">\n      <Title>The type cast is impossible</Title>\n      <Description>Impossible cast (($TARGET_TYPE$)$EXPRESSION$) is performed in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code performs a cast like <code>(($TARGET_TYPE:plain$)$EXPRESSION$)</code>. However it's statically known that\n      this cast will always fail with <code>ClassCastException</code> as the following type constraint holds:<br>\n      <br><code>$EXPRESSION$ :- $ETYPE$</code><br><br>\n      Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ClassComparisonFalse\">\n      <Title>The class comparison is always false</Title>\n      <Description>Comparison $EXPRESSION$.getClass() == $TARGET_TYPE$ is always false.</Description>\n      <LongDescription><![CDATA[This code compares a class of <code>$EXPRESSION$</code> with $TARGET_TYPE$ like <code>$EXPRESSION$.getClass() == $TARGET_TYPE:plain$.class</code>. \n      However it's statically known that this comparison is always false as the following type constraint holds:<br>\n      <br><code>$EXPRESSION$ :- $ETYPE$</code><br><br>\n      Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SameBranchesTernary\">\n      <Title>The ternary operator has identical branches</Title>\n      <Description>The ternary operator has identical branches in $METHOD$.</Description>\n      <LongDescription><![CDATA[Both true and false branches of <code>?:</code> ternary operator has the same code. Probably there's logical error or typo in the code.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SameBranchesIf\">\n      <Title>The if operator has identical branches</Title>\n      <Description>The if operator has identical branches in $METHOD$.</Description>\n      <LongDescription><![CDATA[Both true and false branches of <code>if</code> operator has the same code. Probably there's logical error or typo in the code.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SameBranchesSwitch\">\n      <Title>The switch operator has identical branches</Title>\n      <Description>Two or more switch branches have identical code in $METHOD$.</Description>\n      <LongDescription><![CDATA[Two or more switch branches have identical code. Probably there's logical error or typo in the code. If everything is correct, consider\n   merging these branches together.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SameBranchesSwitchDefault\">\n      <Title>The switch operator branch is identical to the default code</Title>\n      <Description>The switch operator branch is identical to the default code in $METHOD$.</Description>\n      <LongDescription><![CDATA[One or more switch branches have the same code as the default branch (or the code which follows the switch statement). Probably there's logical error or typo in the code. If everything is correct, consider\n   removing these branches as the same code will be executed anyways.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FinalizeOnlyNullsFields\">\n      <Title>The finalize() method only nulls fields</Title>\n      <Description>The $METHOD$ method only nulls fields.</Description>\n      <LongDescription><![CDATA[This finalizer does nothing except null out fields. This is completely pointless, and requires that the object be garbage collected, finalized, and then garbage collected again. You should just remove the finalize method.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FinalizeEmpty\">\n      <Title>The finalize() method is empty</Title>\n      <Description>The $METHOD$ method is empty.</Description>\n      <LongDescription><![CDATA[This finalizer does nothing and you can freely delete it.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FinalizePublic\">\n      <Title>The finalize() method is public</Title>\n      <Description>The $METHOD$ method is public.</Description>\n      <LongDescription><![CDATA[A $METHOD$ method should have protected access, not public.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FinalizeNullsFields\">\n      <Title>The finalize() method nulls fields</Title>\n      <Description>The $METHOD$ method nulls out fields.</Description>\n      <LongDescription><![CDATA[This finalizer nulls out fields. This is usually an error, as it does not aid garbage collection, and the object is going to be garbage collected anyway.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FinalizeNullifiesSuper\">\n      <Title>Finalizer nullifies superclass finalizer</Title>\n      <Description>The $METHOD$ method nullifies superclass finalizer.</Description>\n      <LongDescription><![CDATA[This empty $METHOD$ method explicitly negates the effect of any finalizer defined by its superclass $SUPERCLASS$.\n      Any finalizer actions defined for the superclass will not be performed. Unless this is intended, delete this method.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FinalizeNoSuperCall\">\n      <Title>Finalizer does not call superclass finalizer</Title>\n      <Description>The $METHOD$ method does not call superclass finalizer.</Description>\n      <LongDescription><![CDATA[This $METHOD$ method does not make a call to its superclass's <code>finalize()</code> method. \n      So, finalizer actions defined for the superclass $SUPERCLASS$ will not be performed. Add a call to <code>super.finalize()</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FinalizeUselessSuper\">\n      <Title>Finalizer does nothing except calling the superclass finalizer</Title>\n      <Description>The $METHOD$ method does nothing except calling the superclass finalizer.</Description>\n      <LongDescription><![CDATA[The only thing $METHOD$ does is calling the superclass <code>finalize()</code> method.\n      This is redundant, so this method could be deleted.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ImpossibleToArrayDowncast\">\n      <Title>Impossible downcast of toArray() result</Title>\n      <Description>Impossible downcast of toArray() result in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method casts the result of <code>toArray()</code> method to $TARGET_TYPE$. This will usually fail with <code>ClassCastException</code> as\n      <code>toArray()</code> method returns an <code>Object[]</code> array for most of the collections.<br>\n      The correct way to do this is <code>toArray(new $TARGET_ELEMENT_TYPE:plain$[0])</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"IntegerMultiplicationPromotedToLong\">\n      <Title>Result of integer multiplication promoted to long</Title>\n      <Description>Integer multiplication by $NUMBER$ is promoted to long.</Description>\n      <LongDescription><![CDATA[This method converts the result of integer multiplication to long like <code>long result = x * $NUMBER$</code>. Such operation, depending on the value of <code>x</code> might overflow\n      as the multiplication is performed on 32-bit integers. Probably it was intended to cast before the multiplication like <code>long result = ((long)x) * $NUMBER$</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"IntegerDivisionPromotedToFloat\">\n      <Title>Result of integer division promoted to floating point type</Title>\n      <Description>Result of $SOURCE_TYPE$ division is promoted to $TARGET_TYPE$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method promotes the result of $SOURCE_TYPE$ division to $TARGET_TYPE$ like <code>$TARGET_TYPE$ result = $SOURCE_TYPE$Var / 2</code>. Probably it was intended to cast to the $TARGET_TYPE$ type before the division like\n      <code>$TARGET_TYPE$ result = (($TARGET_TYPE$)$SOURCE_TYPE$Var) / 2</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"IntegerPromotionInCeilOrRound\">\n      <Title>An integer value is cast to float and passed to the rounding method</Title>\n      <Description>A value of $SOURCE_TYPE$ type is promoted to $TARGET_TYPE$ and passed into $CALLED_METHOD$.</Description>\n      <LongDescription><![CDATA[This code tries to round the value which type is already integral, so the rounding operation will unlikely to have an effect. \n      Check the argument of $CALLED_METHOD$.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"WaitNotInLoop\">\n      <Title>Wait not in loop</Title>\n      <Description>The $CALLED_METHOD$ method is called not in the loop in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method contains a call to $CALLED_METHOD$ which is not in a loop.  If the monitor is used for multiple conditions, the condition the caller intended to wait for might not be the one that actually occurred.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"WaitUnconditional\">\n      <Title>Unconditional wait</Title>\n      <Description>Unconditional $CALLED_METHOD$ call in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method contains a call to $CALLED_METHOD$ which is not guarded by conditional control flow.  The code should verify that condition it intends to wait for is not already satisfied before calling wait; any previous notifications will be ignored.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NotifyNaked\">\n      <Title>Naked call to notify() or notifyAll()</Title>\n      <Description>Naked $CALLED_METHOD$ call in $METHOD$.</Description>\n      <LongDescription><![CDATA[ A call to $CALLED_METHOD$ was made without any (apparent) accompanying modification to mutable object state.  In general, calling a notify method on a monitor is done because some condition another thread is waiting for has become true.  However, for the condition to be meaningful, it must involve a heap object that is visible to both threads.<br>\n       This bug does not necessarily indicate an error, since the change to mutable object state may have taken place in a method which then called the method containing the notification.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FinalizeInvocation\">\n      <Title>Explicit invocation of finalizer</Title>\n      <Description>Explicit invocation of finalize() in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method contains an explicit invocation of the <code>finalize()</code> method on an object. Because finalizer methods are supposed to be executed once, and only by the VM, this is a bad idea.<br>\n        If a connected set of objects beings finalizable, then the VM will invoke the finalize method on all the finalizable object, \n        possibly at the same time in different threads. Thus, it is a particularly bad idea, in the finalize method for a class <code>X</code>, invoke finalize on objects referenced by <code>X</code>, because they may already be getting finalized in a separate thread.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SystemRunFinalizersOnExit\">\n      <Title>Method invokes runFinalizersOnExit()</Title>\n      <Description>Invocation of $CALLED_METHOD$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[Never call $CALLED_METHOD$ for any reason. This method is deprecated and inherently unsafe.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SystemGc\">\n      <Title>Method invokes System.gc()</Title>\n      <Description>Invocation of $CALLED_METHOD$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code explicitly invokes garbage collection. Except for specific use in benchmarking, this is very dubious.<br>\n        In the past, situations where people have explicitly invoked the garbage collector in routines such as close or finalize methods has led to huge performance black holes. Garbage collection can be expensive. Any situation that forces hundreds or thousands of garbage collections will bring the machine to a crawl.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SystemExit\">\n      <Title>Method invokes System.exit()</Title>\n      <Description>Invocation of $CALLED_METHOD$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[Invoking $CALLED_METHOD$ shuts down the entire Java virtual machine. This should only been done when it is appropriate. \n      Such calls make it hard or impossible for your code to be invoked by other code. Consider throwing a <code>RuntimeException</code> instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ThreadStopThrowable\">\n      <Title>Method invokes Thread.stop(Throwable)</Title>\n      <Description>Invocation of $CALLED_METHOD$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[The $CALLED_METHOD$ method is inherently unsafe and deprecated. Since Java 8 it just throws <code>UnsupportedOperationException</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"URLBlockingMethod\">\n      <Title>The equals and hashCode methods of URL are blocking</Title>\n      <Description>A $CALLED_METHOD$ is called which may unexpectedly perform a network request.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ method. Such method may perform a DNS request to resolve the domain name which\n      may result in unexpected performance hit. Consider using <code>java.net.URI</code> instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UselessAndWithMinusOne\">\n      <Title>Useless bitwise-and operation with -1</Title>\n      <Description>Useless bitwise-and with -1 ($NUMBER:hex$) in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code performs bit operation like <code>(x &amp; $NUMBER:hex$)</code>. Such operation is useless as the result is always <code>x</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UselessOrWithZero\">\n      <Title>Useless or/xor operation with 0</Title>\n      <Description>Useless or/xor with 0 in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code performs bit operation like <code>(x $OPERATION$ 0)</code>. Such operation is useless as the result is always <code>x</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UselessAndWithZero\">\n      <Title>Useless bitwise-and operation with 0</Title>\n      <Description>Useless bitwise-and with 0 in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code performs bit operation like <code>(x &amp; 0)</code>. Such operation is useless as the result is always zero.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UselessThread\">\n      <Title>A thread was created using the default empty run() method</Title>\n      <Description>A thread was created using the default empty run() method in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates a thread without specifying a <code>run()</code> method either by deriving from the <code>Thread</code> class, or by passing a <code>Runnable</code> object. Thus this thread does nothing.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BooleanConstructor\">\n      <Title>Inefficient Boolean constructor</Title>\n      <Description>Inefficient Boolean constructor is invoked in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates new instances of <code>Boolean</code> class which is just a waste of memory. Only two different <code>Boolean</code> objects are necessary:\n      <code>Boolean.TRUE</code> and <code>Boolean.FALSE</code>. Use $REPLACEMENT$ method which returns one of them.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NumberConstructor\">\n      <Title>Inefficient number constructor</Title>\n      <Description>Inefficient $TARGET_TYPE$ constructor is invoked in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates new instances of $TARGET_TYPE$ class using the constructor. It's usually preferred to use $REPLACEMENT$ method which\n      may return cached object reducing memory usage.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StringConstructor\">\n      <Title>Inefficient string constructor</Title>\n      <Description>Inefficient String(string) constructor is invoked in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates new instances of <code>String</code> class using <code>new String(string)</code>. This is completely unnecessary as the resulting string is indistinguishable from the original string.<br>\n      Before Java 7u6 this could be meaningful to save some memory if the original string was created using the <code>substring()</code>.\n      However currently such operation only wastes memory. Simply use the original string instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StringConstructorEmpty\">\n      <Title>Inefficient empty string constructor</Title>\n      <Description>Inefficient String() constructor is invoked in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates new instances of empty <code>String</code> using <code>new String()</code>. This is completely unnecessary and just wastes memory.\n      Simply use <code>\"\"</code>: you will get the shared instance of empty string.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StringToString\">\n      <Title>Unnecessary call to String.toString() method</Title>\n      <Description>Unnecessary call to $CALLED_METHOD$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ method. This is unnecessary as it just returns the original string.\n      Simply use the original string.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"HashCodeNoEquals\">\n      <Title>Class defines hashCode() but not equals()</Title>\n      <Description>The class $TYPE$ defines hashCode() but not equals().</Description>\n      <LongDescription><![CDATA[This class defines a $METHOD$ method but not an <code>equals()</code> method.\n      Instead, the $SUPER_METHOD$ defined in superclass will be used.\n      Therefore, the class may violate the invariant that equal objects must have equal hashcodes.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"HashCodeObjectEquals\">\n      <Title>Class defines hashCode() and uses Object.equals()</Title>\n      <Description>The class $TYPE$ defines hashCode() and uses Object.equals().</Description>\n      <LongDescription><![CDATA[This class defines a $METHOD$ method but inherits its <code>equals()</code> method \n      from <code>java.lang.Object</code> (which defines equality by comparing object references).\n      Although this will probably satisfy the contract that equal objects must have equal hashcodes, \n      it is probably not what was intended by overriding the <code>hashCode()</code> method.\n      Overriding hashCode() implies that the object's identity is based on criteria more complicated than simple reference equality.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EqualsNoHashCode\">\n      <Title>Class defines equals() but not hashCode()</Title>\n      <Description>The class $TYPE$ defines equals() but not hashCode().</Description>\n      <LongDescription><![CDATA[This class defines a $METHOD$ method but not a <code>hashCode()</code> method.\n      Instead, the $SUPER_METHOD$ defined in superclass will be used.\n      Therefore, the class may violate the invariant that equal objects must have equal hashcodes.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EqualsObjectHashCode\">\n      <Title>Class defines equals() and uses Object.hashCode()</Title>\n      <Description>The class $TYPE$ defines equals() and uses Object.hashCode().</Description>\n      <LongDescription><![CDATA[This class defines a $METHOD$ method but inherits its <code>hashCode()</code> method \n      from <code>java.lang.Object</code> (which returns the identity hash code, an arbitrary value assigned to the object by the VM).\n      Therefore, the class is very likely to violate the invariant that equal objects must have equal hashcodes.\n      If you don't think instances of this class will ever be inserted into a <code>HashMap/HashSet</code>, the recommended hashCode implementation to use is:\n      \n<pre>public int hashCode() {\n   assert false : \"hashCode not designed\";\n   return 42; // any arbitrary constant will do\n}</pre>]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EqualsReturnsFalse\">\n      <Title>The equals() method always returns false</Title>\n      <Description>The $METHOD$ method always returns false.</Description>\n      <LongDescription><![CDATA[This class defines an <code>equals()</code> method that always returns false. This means that an object is not equal to itself, and it is impossible to create useful Maps or Sets of this class. \n        More fundamentally, it means that equals is not reflexive, one of the requirements of the equals method.<br>\n        The likely intended semantics are object identity: that an object is equal to itself. This is the behavior inherited from class Object. If you need to override an equals inherited from a different superclass, you can use use:<br>\n        <code>public boolean equals(Object o) { return this == o; }</code>\n      ]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EqualsReturnsTrue\">\n      <Title>The equals() method always returns true</Title>\n      <Description>The $METHOD$ method always returns true.</Description>\n      <LongDescription><![CDATA[This class defines an equals method that always returns true. This is imaginative, but not very smart. \n      In addition, it means that the equals method is not symmetric.</code>\n      ]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EqualsClassNames\">\n      <Title>The equals() method compares class names</Title>\n      <Description>The $METHOD$ method compares class names.</Description>\n      <LongDescription><![CDATA[This method checks to see if two objects are the same class by checking to see if the names of their classes are equal. You can have different classes with the same name if they are loaded by different class loaders. \n      Just check to see if the class objects are the same like <code>this.getClass() == other.getClass()</code>.\n      ]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EqualsOther\">\n      <Title>The equals() method defined that doesn't override equals(Object)</Title>\n      <Description>The $METHOD$ method doesn't override $NORMAL_EQUALS$.</Description>\n      <LongDescription><![CDATA[The $METHOD$ method doesn't override $NORMAL_EQUALS$. Probably this is a mistake.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EqualsSelf\">\n      <Title>The covariant equals() method defined that doesn't override equals(Object)</Title>\n      <Description>The covariant $METHOD$ method doesn't override $NORMAL_EQUALS$.</Description>\n      <LongDescription><![CDATA[The covariant $METHOD$ method is defined which doesn't override $NORMAL_EQUALS$. Probably this is a mistake.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EqualsEnum\">\n      <Title>The covariant equals() method defined for enum</Title>\n      <Description>The covariant $METHOD$ method is defined for enum.</Description>\n      <LongDescription><![CDATA[This class defines an enumeration, and equality on enumerations is defined using object identity. \n      Defining a covariant equals method $METHOD$ for an enumeration value is exceptionally bad practice, since it would likely result in having two different enumeration values that compare as equals using the covariant enum method, and as not equal when compared normally.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"WrongMapIterator\">\n      <Title>Inefficient use of keySet() iterator instead of entrySet() iterator</Title>\n      <Description>Inefficient use of keySet() iterator instead of entrySet() iterator in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method accesses the value of a <code>Map</code> entry, using a key that was retrieved from a <code>keySet()</code> iterator. \n      It is more efficient to use an iterator on the $REPLACEMENT$ of the map, to avoid the <code>Map.get(key)</code> lookup.<br>\n      In Java-8 you may also consider using <code>Map.forEach()</code> method.\n      ]]></LongDescription>\n    </Warning>\n    <Warning Type=\"WrongMapIteratorValues\">\n      <Title>Inefficient use of keySet() iterator instead of values() iterator</Title>\n      <Description>Inefficient use of keySet() iterator instead of values() iterator in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method accesses the <code>Map</code> values, using a key that was retrieved from a <code>keySet()</code> iterator.\n      It's detected that map key is not used for any purpose other than getting map value. It would be shorter and more efficient to iterate\n      over $REPLACEMENT$ instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ExposeMutableStaticFieldViaParameter\">\n      <Title>Internal static state exposure by storing a mutable object into a static field</Title>\n      <Description>Internal static state exposure by storing an object of $FIELD_TYPE$ type into a $FIELD$.</Description>\n      <LongDescription><![CDATA[This method stores a parameter into a static field $FIELD$. However the field type is $FIELD_TYPE$\n      which allows uncontrolled modification by the caller. Consider copying an object before storing it in the field.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ExposeMutableStaticFieldViaReturnValue\">\n      <Title>Internal static state exposure by returning a mutable static field</Title>\n      <Description>Internal static state exposure by returning a mutable static field $FIELD$ of type $FIELD_TYPE$.</Description>\n      <LongDescription><![CDATA[This method returns a possibly mutable object of type $FIELD_TYPE$ stored in the static field $FIELD$.\n      As a result, uncontrolled modification of the object is possible. Consider returning a copy or wrapping an object\n      into unmodifiable wrapper.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ExposeMutableFieldViaParameter\">\n      <Title>Internal state exposure by storing a mutable object into a field</Title>\n      <Description>Internal state exposure by storing an object of $FIELD_TYPE$ type into a $FIELD$.</Description>\n      <LongDescription><![CDATA[This method stores a parameter into a field $FIELD$. However the field type is $FIELD_TYPE$\n      which allows uncontrolled modification by the caller. Consider copying an object before storing it in the field.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ExposeMutableFieldViaReturnValue\">\n      <Title>Internal state exposure by returning a mutable field</Title>\n      <Description>Internal state exposure by returning a mutable field $FIELD$ of type $FIELD_TYPE$.</Description>\n      <LongDescription><![CDATA[This method returns a possibly mutable object of type $FIELD_TYPE$ stored in the field $FIELD$.\n      As a result, uncontrolled modification of the object is possible. Consider returning a copy or wrapping an object\n      into unmodifiable wrapper.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NewForGetClass\">\n      <Title>Method allocates an object, only to get the class object</Title>\n      <Description>Method creates an object of type $OBJECT_TYPE$ just to get the class object.</Description>\n      <LongDescription><![CDATA[This method retrieves the class object like this: <code>new $OBJECT_TYPE:plain$().getClass()</code>.\n      Allocating the object is completely unnecessary here. Simply writing <code>$OBJECT_TYPE:plain$.class</code> is more performant.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EmptyBranch\">\n      <Title>Empty branch or control flow statement</Title>\n      <Description>Empty branch or control flow statement in $METHOD$.</Description>\n      <LongDescription><![CDATA[An empty control flow statement is found. Probably something else was meant or this statement could be deleted.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ArrayReturnNull\">\n      <Title>Method returns null instead of empty array</Title>\n      <Description>$METHOD$ returns null instead of empty array.</Description>\n      <LongDescription><![CDATA[It is often a better design to return a length zero array rather than a <code>null</code> reference to indicate that there are no results (i.e., an empty list of results). This way, no explicit check for null is needed by clients of the method.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BooleanReturnNull\">\n      <Title>Method with Boolean return type returns explicit null</Title>\n      <Description>Method $METHOD$ with $RETURN_TYPE$ return type returns null.</Description>\n      <LongDescription><![CDATA[A method that returns either <code>Boolean.TRUE</code>, <code>Boolean.FALSE</code> or <code>null</code> is an accident waiting to happen. This method can be invoked as though it returned a value of type boolean, \n      and the compiler will insert automatic unboxing of the $RETURN_TYPE$ value. If a <code>null</code> value is returned, this will result in a <code>NullPointerException</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"OptionalReturnNull\">\n      <Title>Method with Optional return type returns explicit null</Title>\n      <Description>Method $METHOD$ with $RETURN_TYPE$ return type returns null.</Description>\n      <LongDescription><![CDATA[The usage of $RETURN_TYPE$ return type always mean that explicit <code>null</code> returns were not desired by design. \n      Returning a <code>null</code> value in such case is a contract violation and will most likely break clients code.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfMethod\">\n      <Title>Method names should start with a lower case letter</Title>\n      <Description>Name of the method $METHOD$ violates the convention.</Description>\n      <LongDescription><![CDATA[Methods should be named in mixed case (camel-case) with the first letter lowercase and the first letter of each internal word capitalized.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfMethodFutureKeyword\">\n      <Title>Method named as keyword in future Java versions</Title>\n      <Description>Name of the method $METHOD:name$ will become invalid since $JAVA_VERSION$.</Description>\n      <LongDescription><![CDATA[The name of the method <code>$METHOD:name$</code> coincides with keyword which is introduced\n      since $JAVA_VERSION$. This class will have compilation errors when compiled with newer Java.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfMethodSameAsConstructor\">\n      <Title>Method is named like constructor</Title>\n      <Description>Method $METHOD$ was probably intended to be a constructor.</Description>\n      <LongDescription><![CDATA[This regular method $METHOD$ has the same name as the class it is defined in. \n      It is likely that this was intended to be a constructor.\n      If it was intended to be a constructor, remove the declaration of a void return value.\n      If you had accidentally defined this method, realized the mistake, defined a proper constructor but can't get rid of this method due to backwards compatibility, deprecate the method.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfField\">\n      <Title>Field names should start with a lower case letter</Title>\n      <Description>Name of the field $FIELD$ violates the convention.</Description>\n      <LongDescription><![CDATA[Names of fields that are not final should be in mixed case with a lowercase first letter and the first letters of subsequent words capitalized.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfFieldFutureKeyword\">\n      <Title>Field named as keyword in future Java versions</Title>\n      <Description>Name of the field $FIELD:name$ will become invalid since $JAVA_VERSION$.</Description>\n      <LongDescription><![CDATA[The name of the method <code>$FIELD:name$</code> coincides with keyword which is introduced\n      since $JAVA_VERSION$. This class will have compilation errors when compiled with newer Java.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfClass\">\n      <Title>Class names should start with an upper case letter</Title>\n      <Description>Name of the class $TYPE$ violates the convention.</Description>\n      <LongDescription><![CDATA[Class names should be nouns, in mixed case with the first letter of each internal word capitalized. Try to keep your class names simple and descriptive. Use whole words-avoid acronyms and abbreviations (unless the abbreviation is much more widely used than the long form, such as URL or HTML).]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfClassSameAsSuperclass\">\n      <Title>Class simple name is identical to superclass simple name</Title>\n      <Description>$TYPE$ simple name is the same as superclass simple name.</Description>\n      <LongDescription><![CDATA[This class $TYPE$ has a simple name that is identical to that of its superclass $SUPERCLASS$, except that its superclass is in a different package.\n      This can be exceptionally confusing, create lots of situations in which you have to look at import statements to resolve references and creates many opportunities to accidentally define methods that do not override methods in their superclasses.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfClassSameAsInterface\">\n      <Title>Class simple name is identical to interface simple name</Title>\n      <Description>$TYPE$ simple name is the same as interface simple name.</Description>\n      <LongDescription><![CDATA[This class or interface $TYPE$ has a simple name that is identical to that of the implemented interface $INTERFACE$, except that the superinterface is in a different package.\n      This can be exceptionally confusing, create lots of situations in which you have to look at import statements to resolve references and creates many opportunities to accidentally define methods that do not override methods in their superclasses.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfClassException\">\n      <Title>Class is not derived from an Exception, even though it is named as such</Title>\n      <Description>$TYPE$ ends with Exception, but does not inherit Throwable.</Description>\n      <LongDescription><![CDATA[The class $TYPE$ is not derived from another <code>Exception</code> or even <code>Throwable</code>, but ends with 'Exception'. This will be confusing to users of this class.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FloatComparison\">\n      <Title>Test for floating point equality</Title>\n      <Description>Test for floating point equality in $METHOD$.</Description>\n      <LongDescription><![CDATA[This operation compares two floating point values for equality. \n      Because floating point calculations may involve rounding, calculated float and double values may not be accurate.<br>\n      For values that must be precise, such as monetary values, consider using a fixed-precision type such as <code>BigDecimal</code>.\n      For values that need not be precise, consider comparing for equality within some range, for example: <code>if ( Math.abs(x - y) < .0000001 )</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StartInConstructor\">\n      <Title>Constructor invokes Thread.start()</Title>\n      <Description>Constructor invokes $CALLED_METHOD$ in $TYPE$.</Description>\n      <LongDescription><![CDATA[The constructor starts a thread. This is likely to be wrong if the class is ever extended/subclassed, since the thread will be started before the subclass constructor is started.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnsafeGetResource\">\n      <Title>Class.getResource() may return unexpected results in subclasses</Title>\n      <Description>$CALLED_METHOD$ method may return unexpected results in subclasses.</Description>\n      <LongDescription><![CDATA[This code calls <code>this.getClass().$CALLED_METHOD:name$($ARGUMENT$)</code>. If this class is extended in another package,\n      <code>this.getClass()</code> will return another class and this call will likely to fail.<br>\n      Consider replacing this code with <code>$TYPE:plain$.class.$CALLED_METHOD:name$($ARGUMENT$)</code>. If this class is not supposed to be extended, declare it as <code>final</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"HashCodeRemainder\">\n      <Title>Remainder of hashCode() is used as index</Title>\n      <Description>Remainder of $CALLED_METHOD$ is used as index in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code computes a hashCode, and then computes the remainder of that value modulo another value like <code>(x.hashCode() % n)</code>. Since the hashCode can be negative, the result of the remainder operation can also be negative.\n      The result is then used as index which must be non-negative, so this code may fail with <code>IndexOutOfBoundsException</code>.<br>\n      You may fix this code using <code>Math.abs(x.hashCode() % n)</code> instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RandomIntRemainder\">\n      <Title>Remainder of Random.nextInt() is used as index</Title>\n      <Description>Remainder of $CALLED_METHOD$ is used as index in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code computes a random integer, and then computes the remainder of that value modulo another value like <code>($CALLED_METHOD:plain$ % n)</code>. \n      Since the $CALLED_METHOD$ may return negative value, the result of the remainder operation can also be negative.\n      The result is then used as index which must be non-negative, so this code may fail with <code>IndexOutOfBoundsException</code>.<br>\n      You may fix this code using <code>nextInt(n)</code> instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldFromInstanceMethod\">\n      <Title>Write to static field from instance method</Title>\n      <Description>Instance method $METHOD$ writes to static field $FIELD$.</Description>\n      <LongDescription><![CDATA[This instance method writes to a static field $FIELD$. This is tricky to get correct if multiple instances are being manipulated, and generally bad practice.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CatchIllegalMonitorStateException\">\n      <Title>Dubious catching of IllegalMonitorStateException</Title>\n      <Description>Dubious catching of IllegalMonitorStateException in $METHOD$.</Description>\n      <LongDescription><![CDATA[$EXCEPTION$ is generally only thrown in case of a design flaw in your code (calling wait or notify on an object you do not hold a lock on).\n      Normally it should not be catched.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CatchConcurrentModificationException\">\n      <Title>Dubious catching of ConcurrentModificationException</Title>\n      <Description>Dubious catching of ConcurrentModificationException in $METHOD$.</Description>\n      <LongDescription><![CDATA[$EXCEPTION$ is generally only thrown in case of a design flaw in your code (concurrent modification of the collection which is not thread-safe).\n      Normally it should not be catched.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BitCheckGreater\">\n      <Title>Check for sign of bitwise operation result</Title>\n      <Description>Check for sign like (x &amp; $NUMBER:hex$) &gt; 0 in $METHOD$.</Description>\n      <LongDescription><![CDATA[This condition checks the sign of the result of bitwise operation like <code>(x & $NUMBER:hex$) &gt; 0</code>.\n      It's better to use equality check: <code>(x & $NUMBER:hex$) != 0</code> as if bit mask will later change, it may have the high bit set producing the negative result.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BitCheckGreaterNegative\">\n      <Title>Check for sign of bitwise operation result with high bit</Title>\n      <Description>Check for sign like (x &amp; $NUMBER:hex$) &gt; 0 may produce incorrect result.</Description>\n      <LongDescription><![CDATA[This condition checks the sign of the result of bitwise operation like <code>(x & $NUMBER:hex$) &gt; 0</code>.\n      However the <code>$NUMBER:hex$</code> has the high bit set, thus it's actually negative number ($NUMBER:dec$). The result of this comparison\n      could be negative as well which is likely to be unexpected here. Consider replacing with equality check: <code>(x & $NUMBER:hex$) != 0</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BoxedForToString\">\n      <Title>Unnecessary boxing to convert a value to string</Title>\n      <Description>$BOXED_TYPE$ object is created just for toString() call in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method tries to convert a value to string using the pattern like <code>new $BOXED_TYPE:plain$(val).toString()</code>\n      or <code>(($BOXED_TYPE:plain$)val).toString()</code>. Creating boxed object is completely unnecessary here. \n      Though in some cases the object could be optimized out by JIT-compiler, it's still better to use $REPLACEMENT$ method instead:\n      <code>String.valueOf(val)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BoxedForUnboxing\">\n      <Title>Unnecessary boxing to convert a value back to primitive</Title>\n      <Description>$BOXED_TYPE$ object is created just for unboxing in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method unnecessarily creates $BOXED_TYPE$ object which is used for unboxing only. This is unnecessary and may impact the performance.\n      Just use primitive type instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnboxedForBoxing\">\n      <Title>A boxed object is unboxed just for subsequent boxing</Title>\n      <Description>$BOXED_TYPE$ object is unboxed just for subsequent boxing in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method unnecessarily unboxes the $BOXED_TYPE$ object which is used for subsequent boxing only.\n      This is inefficient as additional objects might be allocated which are exact copies of the original object. Consider using the boxed type all the way.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"MethodTooLarge\">\n      <Title>Method body is too large for detailed analysis</Title>\n      <Description>$METHOD$ body is too larget for details analysis.</Description>\n      <LongDescription><![CDATA[Method $METHOD$ bytecode size is $BYTECODE_SIZE$ bytes which is bigger than current limit $LIMIT$ set for bytecode analysis.\n      As a result the method body is not analysed.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadNameOfMethodMistake\">\n      <Title>Probably the method was mistakenly named</Title>\n      <Description>Probably $REPLACEMENT:name$() was intended instead of $METHOD:name$().</Description>\n      <LongDescription><![CDATA[This class defines a method called <code>$METHOD:name$()</code>. This method does not override the $REPLACEMENT$, which is probably what was intended.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NegatingComparatorResult\">\n      <Title>Dangerous comparison result negation</Title>\n      <Description>The result of $CALLED_METHOD$ is negated.</Description>\n      <LongDescription><![CDATA[This code negates a result of $CALLED_METHOD$. The comparison method contract allows to return any integer value, including <code>Integer.MIN_VALUE</code>\n      which sign will not change after negation. It's safer to use <code>b.compareTo(a)</code> instead of <code>-a.compareTo(b)</code> and\n      <code>comparator.compare(b, a)</code> instead of <code>-comparator.compare(a, b)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ComparingComparatorResultWithNumber\">\n      <Title>Comparison result is checked against specific number</Title>\n      <Description>The result of $CALLED_METHOD$ is compared with $NUMBER$.</Description>\n      <LongDescription><![CDATA[This code compares the result of $CALLED_METHOD$ with $NUMBER$. The comparison method contract allows to return any number, so you should not check against\n      specific constants. Instead check the sign of the comparison result.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"DroppedException\">\n      <Title>Exception created and dropped rather than thrown</Title>\n      <Description>Exception $EXCEPTION$ is created and dropped.</Description>\n      <LongDescription><![CDATA[This code creates an exception (or error) object of type $EXCEPTION$, but doesn't do anything with it.\n      Probably the <code>throw</code> keyword is missing.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"IgnoredException\">\n      <Title>Exception is ignored</Title>\n      <Description>Exception $EXCEPTION$ is ignored in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code ignores any exception of type $EXCEPTION$. This is dangerous as may mask\n      programming errors or internal VM errors and make your program silently behaving incorrectly.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ScheduledThreadPoolExecutorChangePoolSize\">\n      <Title>Futile attempt to change max pool size of ScheduledThreadPoolExecutor</Title>\n      <Description>Callind $CALLED_METHOD$ has no effect for $ARG_TYPE$.</Description>\n      <LongDescription><![CDATA[This code tries to change max pool size calling $CALLED_METHOD$. However this method has no effect for $ARG_TYPE$ as this thread pool\n      acts as fixed-sized pool.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"AppendObjectOutputStream\">\n      <Title>Attempt to append to an object output stream</Title>\n      <Description>Attempt to append to an object output stream in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code opens a file in append mode and then wraps the result in an $OOS_TYPE$. This won't allow you to append to an existing $OOS_TYPE$ stored in a file. If you want to be able to append to an object output stream, you need to keep the object output stream open.<br>\n      The only situation in which opening a file in append mode and the writing an $OOS_TYPE$ could work is if on reading the file you plan to open it in random access mode and seek to the byte offset where the append started.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"AndEqualsAlwaysFalse\">\n      <Title>Two conditions cannot be true simultaneously</Title>\n      <Description>A condition like (x.equals($CONST1$) &amp;&amp; x.equals($CONST2$)) is never true.</Description>\n      <LongDescription><![CDATA[This code checks that the same expression is simultaneously equal to two different values <code>$CONST1$</code> and <code>$CONST2$</code>.\n      This condition is never true. Probably it was intended to use || instead of &amp;&amp;.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"OrNotEqualsAlwaysTrue\">\n      <Title>One of two conditions is always true</Title>\n      <Description>A condition like (!x.equals($CONST1$) || !x.equals($CONST2$)) is always true.</Description>\n      <LongDescription><![CDATA[This code checks that the same expression is either not equal to <code>$CONST1$</code> or not equal to <code>$CONST2$</code>.\n      This condition is always true. Probably it was intended to use &amp;&amp; instead of ||.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"DateBadMonth\">\n      <Title>Bad constant value for month</Title>\n      <Description>$CALLED_METHOD$ is called with incorrect argument $NUMBER$.</Description>\n      <LongDescription><![CDATA[A $CALLED_METHOD$ is called, but the argument $NUMBER$ is out of range: it should be from 0 (January) to 11 (December).]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UselessStringSubstring\">\n      <Title>Useless String.substring(0)</Title>\n      <Description>Passing 0 to $CALLED_METHOD$ is useless.</Description>\n      <LongDescription><![CDATA[A $CALLED_METHOD$ is called like <code>str.$CALLED_METHOD:name$(0)</code>. This is useless as the resulting string\n      will be the same as the original one. Remove this call.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StringIndexIsLessThanZero\">\n      <Title>String index is less than zero</Title>\n      <Description>Negative index or length $INDEX$ is passed to $CALLED_METHOD$.</Description>\n      <LongDescription><![CDATA[Negative index or length $INDEX$ is passed to $CALLED_METHOD$ which will result in <code>IndexOutOfBoundsException</code> at runtime.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StringIndexIsGreaterThanAllowed\">\n      <Title>String index is too big</Title>\n      <Description>Index $INDEX$ passed to $CALLED_METHOD$ exceeds maximum possible value of $MAX_VALUE$.</Description>\n      <LongDescription><![CDATA[Index or length $INDEX$ passed to $CALLED_METHOD$ exceeds $MAX_VALUE$ which will result in <code>IndexOutOfBoundsException</code> at runtime.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ComparatorIsNotSerializable\">\n      <Title>Comparator is not serializable</Title>\n      <Description>Comparator $TYPE$ doesn't implement $SHOULD_IMPLEMENT$.</Description>\n      <LongDescription><![CDATA[The $TYPE$ class implements the <code>Comparator</code> interface. Consider implementing also $SHOULD_IMPLEMENT$ interface.<br>\n      If a comparator is used to construct an ordered collection such as a <code>TreeMap</code>, then the <code>TreeMap</code> will be serializable only if the comparator is also serializable. \n      As most comparators have little or no state, making them serializable is generally easy and good defensive programming. ]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SerialVersionUidNotStatic\">\n      <Title>The serialVersionUID field is not static</Title>\n      <Description>The $FIELD$ is not static.</Description>\n      <LongDescription><![CDATA[This class defines a $FIELD$ that is not static.\n       The field should be made static if it is intended to specify the version UID for purposes of serialization.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SerialVersionUidNotFinal\">\n      <Title>The serialVersionUID field is not final</Title>\n      <Description>The $FIELD$ is not final.</Description>\n      <LongDescription><![CDATA[This class defines a $FIELD$ that is not final.\n       The field should be made final if it is intended to specify the version UID for purposes of serialization.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SerialVersionUidNotLong\">\n      <Title>The serialVersionUID field type is not long</Title>\n      <Description>The $FIELD$ must be of long type.</Description>\n      <LongDescription><![CDATA[This class defines a $FIELD$ which type is <code>int</code>.\n       The field type should be changed to <code>long</code> if it is intended to specify the version UID for purposes of serialization.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SerializationMethodMustBePrivate\">\n      <Title>Serialization method must be private</Title>\n      <Description>Serialization method $METHOD$ must be private.</Description>\n      <LongDescription><![CDATA[This class is serializable and defines $METHOD$ for custom serialization or deserialization.\n      But since that method isn't declared private, it will be silently ignored by the serialization/deserialization API.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ReadResolveIsStatic\">\n      <Title>readResolve() method is static</Title>\n      <Description>$METHOD$ must not be static.</Description>\n      <LongDescription><![CDATA[The $METHOD$ is declared as static, thus it will be silently ignored by serialization.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ReadResolveMustReturnObject\">\n      <Title>readResolve() method must return Object</Title>\n      <Description>$METHOD$ must return Object.</Description>\n      <LongDescription><![CDATA[The $METHOD$ return type is not <code>Object</code>, thus it will be silently ignored by serialization.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantInterface\">\n      <Title>Class implements the same interface as superclass</Title>\n      <Description>Class $TYPE$ implements $INTERFACE$ which is already implemented by superclass.</Description>\n      <LongDescription><![CDATA[The $TYPE$ class declares that it implements an interface $INTERFACE$ that is also implemented by a superclass. This is redundant because once a superclass implements an interface, all subclasses by default also implement this interface.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"IteratorHasNextCallsNext\">\n      <Title>The hasNext() method invokes next()</Title>\n      <Description>Method $METHOD$ invokes $CALLED_METHOD$.</Description>\n      <LongDescription><![CDATA[The iterator method $METHOD$ invokes $CALLED_METHOD$. This is almost certainly wrong, since the <code>hasNext()</code> method is not supposed to change the state of the iterator, and the <code>next()</code> method is supposed to change the state of the iterator.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"IteratorNoThrow\">\n      <Title>The next() iterator method does not throw NoSuchElementException</Title>\n      <Description>The $METHOD$ method does not throw NoSuchElementException.</Description>\n      <LongDescription><![CDATA[It looks like the method $METHOD$ does not throw <code>NoSuchElementException</code>. By iterator contract it must throw this exception when there are no\n      elements left.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BitShiftInvalidAmount\">\n      <Title>Suspicious bit count in bit shift operation</Title>\n      <Description>Bit shift operation (x $OPERATION$ $NUMBER$) has suspicious bit count.</Description>\n      <LongDescription><![CDATA[This code performs a bit shift like <code>(x $OPERATION$ $NUMBER$)</code> where number of bits is not in the (0..$MAX_VALUE$) range.\n      In this case only lower bits will be used (e.g. <code>val &gt;&gt; 70</code> is equal to <code>val &gt;&gt; 6</code>). Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BitAddSignedByte\">\n      <Title>Adding a signed byte to the value with low 8 bits clear</Title>\n      <Description>Adding a signed byte to the value with low 8 bits clear in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code adds a signed byte value to another value which has low 8 bits clear like <code>(x << 8) + byteValue</code>.\n      Probably it was intended to add unsigned byte value like <code>(x << 8) + (byteValue &amp; 0xFF)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BitOrSignedByte\">\n      <Title>Bitwise or with a signed byte</Title>\n      <Description>Bitwise or with a signed byte in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code performs bitwise or of a signed byte value with another value which has low 8 bits clear like <code>(x << 8) | byteValue</code>.\n      Usually this is a mistake as if byteValue is negative, it will be widened to <code>0xFFFFFFbb</code> and higher bits\n      will be affected as well. Probably it was intended to use unsigned byte value like <code>(x << 8) | (byteValue &amp; 0xFF)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ComparisonWithOutOfRangeValue\">\n      <Title>Numeric comparison is always true or false</Title>\n      <Description>Numeric comparison like (x $OPERATION$ $NUMBER$) is always $RESULT$.</Description>\n      <LongDescription><![CDATA[This code performs a comparison like (x $OPERATION$ $NUMBER$). However due to the value type or previous operations it's known that <code>x = $MIN_VALUE$..$MAX_VALUE$</code>,\n      thus the result of this comparison is always $RESULT$. Probably there's some logic error or typo.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SwitchBranchUnreachable\">\n      <Title>Switch branch is unreachable due to expression range</Title>\n      <Description>Unreachable switch branch \"case $NUMBER$\".</Description>\n      <LongDescription><![CDATA[The <code>case $NUMBER$:</code> switch branch will never be executed, because it's statically known that\n      switch expression range is <code>$MIN_VALUE$..$MAX_VALUE$</code>. Probably there's some logic error or typo.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RegexBadSyntax\">\n      <Title>Incorrect syntax of regular expression</Title>\n      <Description>Incorrect syntax of regular expression $REGEXP:const$.</Description>\n      <LongDescription><![CDATA[This code uses a regular expression <code>$REGEXP:const$</code> which has the following syntax error:<br>\n      <pre>$ERROR_MESSAGE$</pre>]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RegexUnintended\">\n      <Title>\".\" or \"|\" used for regular expression</Title>\n      <Description>$REGEXP:const$ is used for regular expression.</Description>\n      <LongDescription><![CDATA[This code uses a regular expression <code>$REGEXP:const$</code>. Such regular expression is mostly useless. \n      Probably <code>\"\\\\$REGEXP$\"</code> was intended instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RegexFileSeparator\">\n      <Title>File separator is used for regular expression</Title>\n      <Description>File separator is used for regular expression in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code uses file separator as a regular expression. This will fail on Windows platforms, where the <code>File.separator</code> is a backslash, \n      which is interpreted in a regular expression as an escape character.<br>\n      To fix this code you may use <code>Pattern.quote(File.separator)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ParameterOverwritten\">\n      <Title>Argument is not used and overwritten</Title>\n      <Description>Argument $VARIABLE$ is not used and overwritten in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code method accepts an argument $VARIABLE$, but its initial value\n      is not used and overwritten in the method. This may indicate a logical error in the program.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"IncorrectConcurrentMethod\">\n      <Title>Using monitor style wait methods on java.util.concurrent abstraction</Title>\n      <Description>Method $CALLED_METHOD:name$() is called on $TARGET$ type.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ on object of $TARGET$ type. Probably the correspoding concurrency API method\n      like $REPLACEMENT$ was intended. Even if this code is written as intended, this is a bad practice and might be extremely confusing for\n      other developers.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SynchronizationOnUpdatedField\">\n      <Title>Synchronization on updated field</Title>\n      <Description>Synchronization on updated field $FIELD$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code synchronizes on a $FIELD$ and the field is updated within the synchronization block.\n      This is unlikely to work as expected as different threads may synchronize on different objects.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SynchronizationOnBoolean\">\n      <Title>Synchronization on Boolean</Title>\n      <Description>Synchronization on $MONITOR_TYPE$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code synchronizes on a boxed $MONITOR_TYPE$ value: <code>synchronized($EXPRESSION$)</code>. \n      Since there normally exist only two Boolean objects, this code could be synchronizing on the same object as other, unrelated code, leading to unresponsiveness and possible deadlock.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SynchronizationOnBoxedNumber\">\n      <Title>Synchronization on boxed number</Title>\n      <Description>Synchronization on $MONITOR_TYPE$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code synchronizes on a boxed $MONITOR_TYPE$ value: <code>synchronized($EXPRESSION$)</code>. \n      Since $MONITOR_TYPE$ objects can be cached and shared, this code could be synchronizing on the same object as other, unrelated code, leading to unresponsiveness and possible deadlock.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SynchronizationOnUnsharedBoxed\">\n      <Title>Synchronization on unshared boxed value</Title>\n      <Description>Synchronization on newly-created $MONITOR_TYPE$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code synchronizes on a newly-created $MONITOR_TYPE$ value: <code>synchronized($EXPRESSION$)</code>. \n      This may work correctly, but confusing and prone to errors. For example, applying automatic refactoring IDE feature like 'Remove unnecessary boxing'\n      may silently break your code. It's much better to use <code>new Object()</code> as a lock variable.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EmptySynchronizeBlock\">\n      <Title>Empty synchronization block</Title>\n      <Description>Empty synchronization block in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code contains empty synchronization block. This is almost always a bug: it's hardly possible that such block\n      serves its purpose.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SpinLoopOnField\">\n      <Title>Spin loop on non-volatile field</Title>\n      <Description>Spin loop on non-volatile field $FIELD$.</Description>\n      <LongDescription><![CDATA[This code spins in a loop which reads a field $FIELD$. \n      The compiler may legally hoist the read out of the loop, turning the code into an infinite loop.  \n      The class should be changed so it uses proper synchronization (including wait and notify calls).]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StringConcatInLoop\">\n      <Title>String concatenation in a loop</Title>\n      <Description>Variable $VARIABLE$ is concatenated in loop.</Description>\n      <LongDescription><![CDATA[This code concatenates a string (stored in variable <code>$VARIABLE$</code>) in a loop. This could be really inefficient. Consider using <code>StringBuilder</code> instead.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SwingMethodNotInSwingThread\">\n      <Title>Certain swing methods needs to be invoked in Swing thread</Title>\n      <Description>Swing method $CALLED_METHOD$ is not invoked in the Swing thread.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ not in the Swing thread. \n      The Swing methods show(), setVisible(), and pack() will create the associated peer for the frame. With the creation of the peer, the system creates the event dispatch thread. This makes things problematic because the event dispatch thread could be notifying listeners while pack and validate are still processing. This situation could result in two threads going through the Swing component-based GUI -- it's a serious flaw that could result in deadlocks or other related threading issues. A pack call causes components to be realized. As they are being realized (that is, not necessarily visible), they could trigger listener notification on the event dispatch thread.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ConvertCaseWithDefaultLocale\">\n      <Title>Case conversion is performed with system-default locale</Title>\n      <Description>Case conversion method $CALLED_METHOD$ is called with system-default locale.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$. The result of this method may vary depending on system-default locale.\n      For example, in Turkish locale <code>\"point\".toUpperCase()</code> becomes <code>PO&#304;NT</code> which is undesired in many cases, especially in server\n      environment.<br>\n      Consider using the special method $REPLACEMENT$ which accepts a <code>Locale</code> argument. For example, if English case conversion rules are desired, use\n      <code>$REPLACEMENT:name$(Locale.ENGLISH)</code>. Even if system-default locale is desired it's better to use <code>$REPLACEMENT:name$(Locale.getDefault())</code>\n      to explicitly manifest this.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"MethodReliesOnDefaultEncoding\">\n      <Title>Method relies on default encoding</Title>\n      <Description>The called constructor or method $CALLED_METHOD$ relies on default encoding.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$. The called method or constructor relies on system-default encoding which\n      might be inappropriate, especially on server applications. Consider using another method which allows to specify the encoding explicitly\n      either with string (like <code>\"UTF-8\"</code>) or with <code>Charset</code> object (like <code>StandardCharsets.UTF_8</code>).]]></LongDescription>\n    </Warning>\n    <Warning Type=\"InfiniteRecursion\">\n      <Title>Infinite recursion is detected</Title>\n      <Description>Infinite recursion is detected in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method calls itself in a way that will likely to cause infinite recursion and <code>StackOverflowError</code>.\n      Probably there's some logical mistake here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"InfiniteLoop\">\n      <Title>Infinite loop is detected</Title>\n      <Description>Infinite loop is detected in $METHOD$.</Description>\n      <LongDescription><![CDATA[This method defines a loop with non-trivial condition <code>$EXPRESSION$</code> which is unlikely to change.\n      So this loop can finish only exceptionally. Probably there's some error in the code. If such behavior is intended \n      consider defining infinite loop more explicitly like <code>while(true)</code> or <code>for(;;)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"InvariantLoopCondition\">\n      <Title>Invariant loop condition</Title>\n      <Description>Invariant loop condition in $METHOD$.</Description>\n      <LongDescription><![CDATA[This non-trivial condition <code>$EXPRESSION$</code> defined in a loop is unlikely to change over the loop iterations.\n      Probably there's some error in the code. If such behavior is intended \n      consider moving the condition out of the loop or using explicitly infinite loop like <code>while(true)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"InvariantLoopConditionPart\">\n      <Title>Invariant part of loop condition</Title>\n      <Description>Invariant part of loop condition in $METHOD$.</Description>\n      <LongDescription><![CDATA[The part of loop condition <code>$EXPRESSION$</code> is unlikely to change over the loop iterations.\n      Probably there's some error in the code. If such behavior is intended \n      consider moving the condition out of the loop for code clarity.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CollectionAddedToItself\">\n      <Title>Collection added to itself</Title>\n      <Description>Collection is added to itself in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code adds a collection to itself. This is dangerous as after that collection might work badly. For example,\n      calling <code>hashCode</code> on it may throw <code>StackOverflowError</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"PrimitiveArrayPassedAsVarArg\">\n      <Title>Primitive array is passed as var-arg</Title>\n      <Description>Primitive array is passed to $CALLED_METHOD$ where var-arg is expected.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ and passes a primitive array of type $ARRAY_TYPE$ where var-arg argument is expected.\n      The primitive array will be wrapped into one-element object array which is likely a mistake.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CheckForOddnessFailsForNegative\">\n      <Title>Check for oddness may fail for negative input</Title>\n      <Description>Check for oddness like (x % 2 $OPERATION$ 1) may fail for negative input.</Description>\n      <LongDescription><![CDATA[This code checks for oddness using pattern like <code>(x % 2 $OPERATION$ 1)</code>. This would fail if\n      <code>x</code> happens to be negative. Even if it's impossible in your case the error-proof practice is to replace this code with\n      <code>(x % 2 $REPLACEMENT$ 0)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"AverageComputationCouldOverflow\">\n      <Title>Average computation could overflow</Title>\n      <Description>Average computation could overflow in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code computes average array index in a way which could cause signed integer overflow if sum of indices\n      exceeds 2<sup>31</sup>. To compute average index safely use unsigned right shift: <code>((low + high) &gt;&gt;&gt; 1)</code>]]></LongDescription>\n    </Warning>\n    <Warning Type=\"AnnotationNoRuntimeRetention\">\n      <Title>Useless reflection call for an annotation without RUNTIME retention</Title>\n      <Description>Call to $CALLED_METHOD$ will have no effect as $ANNOTATION$ has no runtime retention.</Description>\n      <LongDescription><![CDATA[This code calls <code>$CALLED_METHOD:name$($ANNOTATION:name$.class)</code>. However the annotation $ANNOTATION$ is\n      not visible to reflection as it does not annotated with <code>@Retention(RetentionPolicy.RUNTIME)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"DeadStoreInReturn\">\n      <Title>Useless local variable update in return statement</Title>\n      <Description>Useless update of variable $VARIABLE$ in return statement.</Description>\n      <LongDescription><![CDATA[This code updates local variable $VARIABLE$ and immediately returns. Updating local variable is useless here as the updated value is never used.\n      Probably something else was meant here or assignment could be removed.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"DeadIncrementInReturn\">\n      <Title>Useless increment of local variable in return statement</Title>\n      <Description>Useless increment of variable $VARIABLE$ in return statement.</Description>\n      <LongDescription><![CDATA[This code increments local variable $VARIABLE$ and immediately returns. Updating local variable is useless here as the updated value is never used.\n      Probably something else was meant here or increment statement could be removed or replaced with addition.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"DeadIncrementInAssignment\">\n      <Title>Increment result is nullified by assignment</Title>\n      <Description>Useless increment in expression: $EXPRESSION$.</Description>\n      <LongDescription><![CDATA[This code increments local variable $VARIABLE$ and immediately assigns it to the same variable\n      like <code>$EXPRESSION$</code>. This makes the increment completely useless.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CloneableDoesNotImplementClone\">\n      <Title>Class implements Cloneable, but does not have clone() method</Title>\n      <Description>Class $TYPE$ implements Cloneable, but does not have clone() method.</Description>\n      <LongDescription><![CDATA[This class implements <code>Cloneable</code> interface, but does not have <code>clone()</code> method. Usually it's a mistake, especially if\n      the class defines fields.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CloneableNoSuperCall\">\n      <Title>The clone() method does not call super.clone()</Title>\n      <Description>Method $METHOD$ does not call super.clone().</Description>\n      <LongDescription><![CDATA[This non-final class defines a $METHOD$ method that does not call <code>super.clone()</code>. If this class $TYPE$ is extended by a subclass (<code>$TYPE:name$Sub</code>), \n      and the subclass <code>$TYPE:name$Sub</code> calls <code>super.clone()</code>, \n      then it is likely that <code>$TYPE:name$Sub</code>'s <code>clone()</code> method will return an object of type $TYPE$, which violates the standard contract for <code>clone()</code>.<br>\n      If all <code>clone()</code> methods call <code>super.clone()</code>, then they are guaranteed to use <code>Object.clone()</code>, which always returns an object of the correct type.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NotCloneableHasClone\">\n      <Title>Class defines clone() but doesn't implement Cloneable</Title>\n      <Description>Class $TYPE$ defines clone() but doesn't implement Cloneable.</Description>\n      <LongDescription><![CDATA[This class defines a $METHOD$ method but the class doesn't implement <code>Cloneable</code>. This could be confusing for the users of this class.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SyncOnGetClass\">\n      <Title>Synchronization on getClass() rather than class literal</Title>\n      <Description>Synchronization on $CALLED_METHOD$ rather than class literal.</Description>\n      <LongDescription><![CDATA[This instance method synchronizes like <code>synchronized(this.getClass()) {...}</code>. \n      If this class is subclassed, subclasses will synchronize on the class object for the subclass, which isn't likely what was intended.<br>\n      Consider synchronizing on concrete class literal instead: <code>synchronized($TYPE:name$.class) {...}</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UselessEasyMockCall\">\n      <Title>Useless call to EasyMock method</Title>\n      <Description>Useless call to $CALLED_METHOD$.</Description>\n      <LongDescription><![CDATA[This code calls EasyMock method $CALLED_METHOD$ with empty argument. Such call will do nothing.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ReturnValueOfRead\">\n      <Title>Return value of read() method is not checked</Title>\n      <Description>Return value of $CALLED_METHOD$ is not checked.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$, but does not check its return value. By contract, this method may read less\n      data that requested and returns the number of bytes or characters which were actually read. Without checking the return value it's\n      not possible to reliably tell how many bytes or characters were actually read.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ReturnValueOfSkip\">\n      <Title>Return value of skip() method is not checked</Title>\n      <Description>Return value of $CALLED_METHOD$ is not checked.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$, but does not check its return value. By contract, this method may skip less\n      data that requested and returns the number of bytes or characters which were actually skipped. Without checking the return value it's\n      not possible to reliably tell how many bytes or characters were actually skipped.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NullCheckMethodForConstant\">\n      <Title>Null-check method called with constant non-null argument</Title>\n      <Description>$CALLED_METHOD$ called with constant argument.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ which checks the argument for null.\n      However it's statically known that constant non-null argument is passed, so this check will always succeed.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"WrongArgumentOrder\">\n      <Title>Arguments order seems to be wrong</Title>\n      <Description>$CALLED_METHOD$ called with wrong argument order.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ which checks the argument for null and fails with given message\n      if the check is not satisfied. However it seems that message argument and object argument are mistakenly swapped which\n      makes this check useless.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UncalledPrivateMethod\">\n      <Title>Private method is never called</Title>\n      <Description>The method $METHOD$ is private and never called.</Description>\n      <LongDescription><![CDATA[The private method $METHOD$ is never explicitly called and probably should be removed.\n      If it's designed to be called via reflection or via method handles API, \n      it's a good practice to annotate it by some annotation which will explicitly signal that the method is necessary.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UncalledMethodOfAnonymousClass\">\n      <Title>A method of anonymous class is never called</Title>\n      <Description>The method $METHOD$ of anonymous class is never called.</Description>\n      <LongDescription><![CDATA[The method $METHOD$ of anonymous class is never used and probably should be removed.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UncalledPrivateMethodChain\">\n      <Title>The chain of private methods is never called</Title>\n      <Description>The method $METHOD$ along with some other methods is never called.</Description>\n      <LongDescription><![CDATA[The private method $METHOD$ along with some other private methods form a chain which is never called\n      (though the methods in the chain may call each other). Probably the whole chain should be removed.\n      If it's designed to be called via reflection or via method handles API, \n      it's a good practice to annotate it by some annotation which will explicitly signal that the method is necessary.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ArrayIndexNegative\">\n      <Title>Negative array index or length used</Title>\n      <Description>Negative array index or length used: $NUMBER$.</Description>\n      <LongDescription><![CDATA[This code uses $NUMBER$ to specify array index, offset or length. This would result\n      \tin <code>IndexOutOfBoundsException</code> exception in runtime.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ArrayIndexOutOfRange\">\n      <Title>Array index is out of bounds</Title>\n      <Description>Used array index $NUMBER$ exceeds or equals to possible array length $MAX_LENGTH$.</Description>\n      <LongDescription><![CDATA[This code uses $NUMBER$ to specify array index. However it's statically known that given array\n        length do not exceed $MAX_LENGTH$. This would result in <code>ArrayIndexOutOfBoundsException</code> in runtime.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ArrayOffsetOutOfRange\">\n      <Title>Array offset is out of bounds</Title>\n      <Description>Used array offset $NUMBER$ exceeds possible array length $MAX_LENGTH$.</Description>\n      <LongDescription><![CDATA[This code uses $NUMBER$ to specify array offset. However it's statically known that given array\n        length do not exceed $MAX_LENGTH$. This would result in <code>IndexOutOfBoundsException</code> in runtime.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ArrayLengthOutOfRange\">\n      <Title>Array length is out of bounds</Title>\n      <Description>Specified array length $NUMBER$ exceeds maximal possible value $MAX_LENGTH$.</Description>\n      <LongDescription><![CDATA[This code specifies length = $NUMBER$, but it's statically known that given array\n        length should not exceed $MAX_LENGTH$. This would result in <code>IndexOutOfBoundsException</code> in runtime.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ContravariantArrayStore\">\n      <Title>Contravariant store to an array</Title>\n      <Description>Attempt to store value of type $VALUE_TYPE$ to an array of type $ARRAY_TYPE$.</Description>\n      <LongDescription><![CDATA[This code stores the value of type $VALUE_TYPE$ into an array of type $ARRAY_TYPE$.\n      It seems that these types are incompatible, thus this operation may cause <code>ArrayStoreException</code> at runtime.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FieldDoubleAssignment\">\n      <Title>Field is assigned twice</Title>\n      <Description>The field $FIELD$ is assigned twice in a row.</Description>\n      <LongDescription><![CDATA[This code assigns the value to the field $FIELD$ twice in a row making the first assignment useless.\n      Probably something else was intended.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnusedPublicField\">\n      <Title>Public or protected field is unused</Title>\n      <Description>Public or protected field $FIELD$ is unused.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is unused and could be removed. False positive is possible if it's intended to be used\n      in the code which is out of the scope of this analysis or used via reflection.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnusedPrivateField\">\n      <Title>Private or package-private field is unused</Title>\n      <Description>Private or package-private field $FIELD$ is unused.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is unused and could be removed. If it's intended to be used via reflection, consider annotating it\n      with some annotation to avoid confusion.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnreadPublicField\">\n      <Title>Public or protected field is never read</Title>\n      <Description>Public or protected field $FIELD$ is written, but never read.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is written, but never read, so probably it should be removed. \n      False positive is possible if it's intended to be read in the code which is out of the scope of this analysis \n      or used via reflection.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnreadPrivateField\">\n      <Title>Private or package-private field is never read</Title>\n      <Description>Private or package-private field $FIELD$ is written, but never read.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is written, but never read, so probably it should be removed. \n      If it's intended to be read via reflection, consider annotating it with some annotation to avoid confusion.\n      False-positive is possible if the field is used only to keep strong reference to the object.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FieldShouldBeStatic\">\n      <Title>Non-static final field is initialized to a static value</Title>\n      <Description>Non-static final field $FIELD$ is initialized to a static value.</Description>\n      <LongDescription><![CDATA[The final field $FIELD$ is initialized to a statically known value.\n      Consider making this field static.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FieldUsedInSingleMethod\">\n      <Title>Field could be refactored to local variable</Title>\n      <Description>Field $FIELD$ could be refactored to local variable.</Description>\n      <LongDescription><![CDATA[Analysis discovers that field $FIELD$ is used only inside method $METHOD$ and\n      the value stored after method execution is never used again. Consider converting this field into local variable \n      to reduce the object footprint and possibly limit the lifespan of stored object.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnwrittenPublicField\">\n      <Title>Public or protected field is used, but never written</Title>\n      <Description>Public or protected field $FIELD$ is used, but never written.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is used, but never written, so it always has default value. \n      False positive is possible if it's intended to be written in the code which is out of the scope of this analysis \n      or written via reflection.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnwrittenPrivateField\">\n      <Title>Private or package-private field is used, but never written</Title>\n      <Description>Private or package-private field $FIELD$ is used, but never written.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is used, but never written, so it always has default value. \n      If it's intended to be written (injected) via reflection, consider annotating it with some annotation to avoid confusion.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"FieldIsAlwaysNull\">\n      <Title>Field only ever set to null</Title>\n      <Description>Field $FIELD$ only ever set to null.</Description>\n      <LongDescription><![CDATA[All writes to this field $FIELD$ are of the constant value <code>null</code>, \n      and thus all reads of the field will return <code>null</code>. Probably something else was meant or this field can be removed. \n      If it's intended to be written (injected) via reflection, consider annotating it with some annotation to avoid confusion.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"InitializerRefersSubclass\">\n      <Title>Class initializer refers to subclass</Title>\n      <Description>Initializer of $TYPE$ refers to its subclass $SUBCLASS$.</Description>\n      <LongDescription><![CDATA[The class initializer of $TYPE$ may trigger the initialization of its subclass $SUBCLASS$.\n      This might cause JVM-level deadlock when both classes are initialized simultaneously from different threads.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantStreamForEach\">\n      <Title>Redundant stream created just to call forEach</Title>\n      <Description>Redundant stream created just to call $CALLED_METHOD$ in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates a stream upon collection just to call $CALLED_METHOD$.\n      It's shorter and usually more efficient to call $REPLACEMENT$ directly on the collection.\n      Note that sometimes <code>collection.stream().forEach()</code> and <code>collection.forEach()</code> have different semantics.\n      For example, for <code>Collections.synchronizedXYZ</code> collections <code>stream().forEach()</code> is not synchronized,\n      but <code>forEach()</code> is synchronized.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantCollectionStream\">\n      <Title>Redundant collection is created just to make stream</Title>\n      <Description>Redundant collection is created just to make stream in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates a redundant collection just to create a stream.\n      Here stream can be created directly using <code>$REPLACEMENT$</code>. Which is shorter and probably more performant.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StreamCountFromCollection\">\n      <Title>Stream.count() is called for stream created directly from collection</Title>\n      <Description>Stream.count() is called for stream created directly from collection in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates a srteam from collection just to count its elements.\n      In Java 8 count() method actually enumerates all stream elements, which may be slow for big collections.\n      Use $REPLACEMENT$ which is shorter and usually works faster.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantStreamFind\">\n      <Title>Redundant filter().findFirst().isPresent() chain</Title>\n      <Description>Redundant filter().findFirst().isPresent() chain in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code tries to check whether stream contains element matching the predicate\n      using the sequence like <code>filter(predicate).findFirst().isPresent()</code>. There's dedicated method\n      for such sequence: $REPLACEMENT$. Simply use <code>anyMatch(predicate)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldShouldBeFinal\">\n      <Title>Static field should be final</Title>\n      <Description>Static field $FIELD$ should be final.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is not final, but could be easily declared final to prevent\n      possible malicious changes from other packages.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldShouldBeFinalAndPackagePrivate\">\n      <Title>Static field should be final and package-private</Title>\n      <Description>Static field $FIELD$ should be final and package-private.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is not final, but could be easily declared final to prevent\n      possible malicious changes from other packages. It's also mutable, so can be modified even if declared as final.\n      However as it's not accessed from other packages, you may declare it package-private to prevent undesired access.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldCannotBeFinal\">\n      <Title>Static field cannot be final</Title>\n      <Description>Static field $FIELD$ cannot be final.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is not final and could be modified from other packages.\n      As this final is written after class initialization, it's quite hard to make it final. \n      Also this field is already accessed from other packages, so you cannot easily make it package-private.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldMutableArray\">\n      <Title>Static field is a mutable array</Title>\n      <Description>Static field $FIELD$ is a mutable array.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is a mutable array and could be modified from other packages.\n      This field is already accessed from other packages, so you cannot easily make it package-private.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldMutable\">\n      <Title>Static field is mutable</Title>\n      <Description>Static field $FIELD$ is mutable.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ belongs to the mutable type $FIELD_TYPE$ and could be modified from other packages.\n      This field is already accessed from other packages, so you cannot easily make it package-private.\n      Probably it's possible to make the $FIELD_TYPE$ immutable.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldMutableCollection\">\n      <Title>Static field is a mutable collection</Title>\n      <Description>Static field $FIELD$ is a mutable collection.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is a mutable collection and could be modified from other packages.\n      This field is already accessed from other packages, so you cannot easily make it package-private.\n      However you may consider protect malicious writes using one of <code>Collections.unmodifiableXYZ()</code> methods.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldShouldBePackagePrivate\">\n      <Title>Static field should be package-private</Title>\n      <Description>Static field $FIELD$ should be package-private.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is mutable and could be modified from other packages.\n      Currently it's not used from other packages, so consider making it package-private to prevent unwanted access.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldShouldBeNonInterfacePackagePrivate\">\n      <Title>Static field should be moved out of interface and declared package-private</Title>\n      <Description>Static field $FIELD$ should be moved out of interface and declared package-private.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is mutable and could be modified from other packages.\n      Currently it's not used from other packages, but it's declared in the interface \n      which does not allow to make it package-private.\n      Consider moving this field to some class and declare as package-private.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticFieldShouldBeRefactoredToFinal\">\n      <Title>Static field could be refactored to be final</Title>\n      <Description>Static field $FIELD$ could be refactored to be final.</Description>\n      <LongDescription><![CDATA[The field $FIELD$ is not final and could be modified from other packages.\n      Analysis shows that it should not be very hard to make this field final, though some refactoring is probably required.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ConstructorParameterIsNotPassed\">\n      <Title>Constructor parameter is not passed</Title>\n      <Description>Constructor calls another constructor, but doesn't pass the parameter $VARIABLE$.</Description>\n      <LongDescription><![CDATA[The constructor of class $TYPE$ calls another constructor, \n      but parameter <code>$VARIABLE$</code> is not passed through and is not used in any other way.\n      Probably there's a mistake.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"MethodParameterIsNotPassed\">\n      <Title>Parameter is not passed to the overloaded method</Title>\n      <Description>Method $METHOD$ doesn't pass the parameter $VARIABLE$ to the overloaded method.</Description>\n      <LongDescription><![CDATA[The method $METHOD$ calls another method with the same name, \n      but parameter <code>$VARIABLE$</code> is not passed through and is not used in any other way.\n      Probably there's a mistake.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"MethodParameterIsNotUsed\">\n      <Title>Method parameter is not used</Title>\n      <Description>Method $METHOD$ doesn't use its parameter $VARIABLE$.</Description>\n      <LongDescription><![CDATA[The method $METHOD$ does not use its parameter <code>$VARIABLE$</code>.\n      Probably this parameter could be removed.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"CompareReturnsMinValue\">\n      <Title>Comparison method returns Integer.MIN_VALUE</Title>\n      <Description>Method $METHOD$ returns Integer.MIN_VALUE.</Description>\n      <LongDescription><![CDATA[The comparison method $METHOD$ explicitly returns <code>Integer.MIN_VALUE</code>.\n      While this is perfectly allowed by method contract, it may cause unexpected errors when\n      user tries to negate comparison result. Comparison method can return any negative number here,\n      so better to use something else like <code>-1</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"DeadParameterStore\">\n      <Title>Parameter is assigned to a value which is never used</Title>\n      <Description>Parameter $VARIABLE$ is assigned to a value which is never used.</Description>\n      <LongDescription><![CDATA[This code assigns a new value to the parameter <code>$VARIABLE$</code> and this value\n      is never used subsequently. Note that the assigned value will not be returned to the caller as \n      Java is pass-by-value language.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"DeadLocalStore\">\n      <Title>Local variable is assigned to a value which is never used</Title>\n      <Description>Local variable $VARIABLE$ is assigned to a value which is never used.</Description>\n      <LongDescription><![CDATA[This code assigns a new value to the local variable <code>$VARIABLE$</code> and this value\n      is never used subsequently. Probably something else was meant or the assignment could be removed.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnusedLocalVariable\">\n      <Title>Local variable is never used</Title>\n      <Description>Local variable $VARIABLE$ is never used.</Description>\n      <LongDescription><![CDATA[This code contains a local variable <code>$VARIABLE$</code> which is assigned but never used.\n      Probably it could be removed.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"SameConditionChain\">\n      <Title>The same condition is checked twice in a row</Title>\n      <Description>The same condition is checked twice in $METHOD$.</Description>\n      <LongDescription><![CDATA[This code contains two consequtive <code>if</code> statements like <code>if ($EXPRESSION$) {...} if ($SAME_EXPRESSION$) {...}</code>.\n      Analysis found that both conditions are equivalent. Probably there is some typo. If such logic is intended, consider\n      merging both <code>if</code> statements into one to avoid confusion.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"EqualsSuspiciousFieldComparison\">\n      <Title>Suspicious field comparison in equals method</Title>\n      <Description>Field $FIELD$ is compared with $OTHER_FIELD$ in equals method.</Description>\n      <LongDescription><![CDATA[This equals method compares different fields: $FIELD$ and $OTHER_FIELD$.\n      Usually <code>equals()</code> method compares the same field of this and other object. \n      Probably there's some mistake.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BitShiftWrongPriority\">\n      <Title>Possible bad parsing of shift operation</Title>\n      <Description>Possible incorrect parsing of expression like (x &lt;&lt; $NUMBER$ + y).</Description>\n      <LongDescription><![CDATA[The code performs an operation like <code>(x &lt;&lt; $NUMBER$ + y)</code>. \n      Although this might be correct, probably it was meant to perform <code>(x &lt;&lt; $NUMBER$) + y</code>, \n      but shift operation has a lower precedence, so it's actually parsed as <code>x &lt;&lt; ($NUMBER$ + y)</code>.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NonFinalFieldInImmutableClass\">\n      <Title>Non-final field is declared in immutable class</Title>\n      <Description>Non-final field $FIELD$ is declared in immutable class $TYPE$.</Description>\n      <LongDescription><![CDATA[The class $TYPE$ is annotated as <code>@Immutable</code>. However it \n      declares the mutable field $FIELD$. Make this field either final or transient.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ExceptionalExpression\">\n      <Title>Expression execution will always fail</Title>\n      <Description>Execution of this code will always throw $EXCEPTION$.</Description>\n      <LongDescription><![CDATA[Analysis found that the expression <code>$EXPRESSION$</code>, when executed,\n      will likely to throw $EXCEPTION$. Probably something else was intended here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"AbandonedStream\">\n      <Title>Java stream is abandoned</Title>\n      <Description>The $CALLED_METHOD:return_type.plain$ is abandoned after calling $CALLED_METHOD$.</Description>\n      <LongDescription><![CDATA[This code creates $CALLED_METHOD:return_type.html$ and does not use it afterwards.\n      In most of the cases stream produces no effect unless you call a terminal operation. Probably there's some mistake.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StreamMethodMayNotReturnItself\">\n      <Title>Stream method may not return itself by specification</Title>\n      <Description>The $CALLED_METHOD:return_type.plain$ may not return itself by specification.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ and does not use its result.\n      While it works in current OpenJDK implementation as this Stream method always returns itself,\n      the specification does not guarantee such behavior. It's better to use the resulting stream.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UselessVoidMethod\">\n      <Title>Useless void method</Title>\n      <Description>The method $METHOD$ seems to do nothing useful.</Description>\n      <LongDescription><![CDATA[Analysis discovers that void method $METHOD$ has non-trivial body, yet does nothing useful.\n      Probably there's some mistake.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"MutableEnumField\">\n      <Title>Mutable public enum field</Title>\n      <Description>The public enum field $FIELD$ can be modified by malicious code.</Description>\n      <LongDescription><![CDATA[The enum field $FIELD$ can be uncontrollably modified by anybody affecting the global state\n      of your application. Consider declaring it private or protect writes to it in some other way.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadResultSetArgument\">\n      <Title>ResultSet method is called with zero columnIndex</Title>\n      <Description>The $CALLED_METHOD$ is called with zero columnIndex.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ passing zero (or variable which could be zero\n      on some path) as columnIndex. This is incorrect as columns are numbered starting from 1.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"BadPreparedStatementArgument\">\n      <Title>PreparedStatement method is called with zero parameterIndex</Title>\n      <Description>The $CALLED_METHOD$ is called with zero parameterIndex.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$ passing zero (or variable which could be zero\n      on some path) as parameterIndex. This is incorrect as parameters are numbered starting from 1.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnsupportedCall\">\n      <Title>Unsupported method call</Title>\n      <Description>The $CALLED_METHOD$ is unsupported.</Description>\n      <LongDescription><![CDATA[This code calls $CALLED_METHOD$. However analysis found that this method\n      as well as and its overrides (if applicable) unconditionally throw <code>UnsupportedOperationException</code>,\n      so it's likely that this call will fail.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticNotThreadSafeField\">\n      <Title>Static field is not thread-safe</Title>\n      <Description>Static field $FIELD$ is declared which type $FIELD_TYPE$ is not thread-safe.</Description>\n      <LongDescription><![CDATA[This code declares $FIELD$ of type $FIELD_TYPE$. This type is not thread-safe,\n      so using such object from different threads may cause random exceptions or incorrect results.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"StaticNotThreadSafeFieldInvoke\">\n      <Title>Static field usage is not thread-safe</Title>\n      <Description>Static field $FIELD$ is used which type $CALLED_METHOD:type.plain$ is not thread-safe.</Description>\n      <LongDescription><![CDATA[This code calls method $CALLED_METHOD$ on field $FIELD$. The type $CALLED_METHOD:type.html$ is not thread-safe,\n      so using such object from different threads may cause random exceptions or incorrect results.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"UnreachableCatch\">\n      <Title>Catch block is unreachable</Title>\n      <Description>Catch block is unreachable in $METHOD$.</Description>\n      <LongDescription><![CDATA[Analysis discovers that this catch block is possibly redundant as the corresponding\n      exception type is unlikely to be thrown from the corresponding try block or already catched by previous catch blocks.\n      Probably something else was intended or the catch block could be removed.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"MutableServletField\">\n      <Title>Mutable servlet field</Title>\n      <Description>Mutable servlet field $FIELD$.</Description>\n      <LongDescription><![CDATA[This servlet class declares a mutable field $FIELD$.\n      A web server generally only creates one instance of servlet or jsp class (i.e., treats the class as a singleton), \n      and will have multiple threads invoke methods on that instance to service multiple simultaneous requests. \n      Thus, having a mutable instance field generally creates race conditions.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NullDereferenceGuaranteed\">\n      <Title>Guaranteed null dereference</Title>\n      <Description>Guaranteed null dereference in $METHOD$.</Description>\n      <LongDescription><![CDATA[The expression <code>$EXPRESSION$</code> will fail with <code>NullPointerException</code>\n      as <code>$NULL_EXPRESSION$</code> is guaranteed to be null at this location.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NullDereferenceExceptional\">\n      <Title>Possible null dereference on exceptional path</Title>\n      <Description>Possible null dereference on exceptional path in $METHOD$.</Description>\n      <LongDescription><![CDATA[The expression <code>$EXPRESSION$</code> will fail with <code>NullPointerException</code>\n      if previously executed code goes through catch block as <code>$NULL_EXPRESSION$</code> can be null at this location if exception occurs.\n      This could be false-positive if the catch block always throws implicitly.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"NullDereferencePossible\">\n      <Title>Possible null dereference</Title>\n      <Description>Possible null dereference in $METHOD$.</Description>\n      <LongDescription><![CDATA[The expression <code>$EXPRESSION$</code> may fail with <code>NullPointerException</code>\n      as <code>$NULL_EXPRESSION$</code> can be null at this location.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ImpossibleInstanceOfNull\">\n      <Title>Impossible instanceOf check of null</Title>\n      <Description>The expression $EXPRESSION$ is always false as argument is null.</Description>\n      <LongDescription><![CDATA[The expression <code>$EXPRESSION$</code> is always false as \n      <code>$NULL_EXPRESSION$</code> is always null at this location. Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantNullCheck\">\n      <Title>Redundant null check</Title>\n      <Description>Redundant comparison of $NONNULL_EXPRESSION$ with null.</Description>\n      <LongDescription><![CDATA[The expression <code>$NONNULL_EXPRESSION$</code> is always not null\n      at this place, thus the comparison with null is redundant. Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantNullCheckDeref\">\n      <Title>Redundant null check of previously dereferenced object</Title>\n      <Description>Redundant null check of $NONNULL_EXPRESSION$ which was dereferenced previously.</Description>\n      <LongDescription><![CDATA[The expression <code>$NONNULL_EXPRESSION$</code> is always not null\n      at this place, because it was dereferenced previously. Thus the comparison with null is redundant. Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantNullCheckChecked\">\n      <Title>Redundant null check of previously checked object</Title>\n      <Description>Redundant null check of $NONNULL_EXPRESSION$ which was already checked.</Description>\n      <LongDescription><![CDATA[The expression <code>$NONNULL_EXPRESSION$</code> is always not null\n      at this place, because it was already checked previously. Thus the comparison with null is redundant. Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantNullCheckNull\">\n      <Title>Redundant null check of value known to be null</Title>\n      <Description>Redundant null check of $NULL_EXPRESSION$ which is known to be null.</Description>\n      <LongDescription><![CDATA[The expression <code>$NULL_EXPRESSION$</code> is always null\n      at this place. Thus the comparison with null is redundant. Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantComparisonNull\">\n      <Title>Redundant comparison of two values both of which are null</Title>\n      <Description>Redundant comparison $EXPRESSION$ where both arguments are known to be null.</Description>\n      <LongDescription><![CDATA[The comparison <code>$EXPRESSION$</code> is redundant as both\n      left and right parts are null at this place. Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantComparisonNullNonNull\">\n      <Title>Redundant comparison of null and non-null value</Title>\n      <Description>Redundant comparison $EXPRESSION$ where null is compared to non-null.</Description>\n      <LongDescription><![CDATA[The comparison <code>$EXPRESSION$</code> is redundant as it's known\n      that <code>$NONNULL_EXPRESSION$</code> is always non-null at this place while <code>$NULL_EXPRESSION$</code>\n      is always null. Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"RedundantEqualsNullCheck\">\n      <Title>Redundant equals() call with null argument</Title>\n      <Description>Redundant $EXPRESSION$ where argument is known to be null.</Description>\n      <LongDescription><![CDATA[The equals call <code>$EXPRESSION$</code> is redundant as it's known\n      that the argument <code>$NULL_EXPRESSION$</code> is always null at this place. \n      Probably something else was meant here.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ReadObjectIsSynchronized\">\n      <Title>The readObject() method is synchronized</Title>\n      <Description>The $METHOD$ is synchronized.</Description>\n      <LongDescription><![CDATA[This serializable class defines a <code>readObject()</code> which is\n      synchronized. By definition, an object created by deserialization\n      is only reachable by one thread, and thus there is no need for\n      <code>readObject()</code> to be synchronized. If the <code>readObject()</code>\n      method itself is causing the object to become visible to another thread,\n      that is an example of very dubious coding style.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"WriteObjectIsSynchronized\">\n      <Title>The writeObject() method is synchronized</Title>\n      <Description>The $METHOD$ is synchronized.</Description>\n      <LongDescription><![CDATA[This serializable class defines a <code>writeObject()</code> which is\n      synchronized, but no other method of this class is synchronized as well. \n      Probably this synchronization is useless and could be removed.]]></LongDescription>\n    </Warning>\n    <Warning Type=\"ConcurrentCollectionSize\">\n      <Title>The size() method is called for concurrent collection</Title>\n      <Description>The size() method is called for $TARGET_TYPE$ which is slow.</Description>\n      <LongDescription><![CDATA[This code calls the <code>size()</code> method on concurrent collection\n      of type $TARGET_TYPE$. This is rarely a good idea as this method actually traverses\n      the whole collection which is really slow. Also if collection is modified concurrently,\n      it's size could be inaccurate.]]></LongDescription>\n    </Warning>\n  </WarningList>\n</p:Messages>\n"
  },
  {
    "path": "huntbugs/src/main/resources/huntbugs/messages.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsd:schema elementFormDefault=\"unqualified\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"https://raw.githubusercontent.com/amaembo/huntbugs/master/huntbugs/src/main/resources/huntbugs\" targetNamespace=\"https://raw.githubusercontent.com/amaembo/huntbugs/master/huntbugs/src/main/resources/huntbugs\">\n\n    <xsd:annotation>\n     <xsd:documentation>HuntBugs warning descriptions</xsd:documentation></xsd:annotation>\n\n    \n    <xsd:complexType name=\"WarningList\">\n     <xsd:sequence>\n      <xsd:element name=\"Warning\" type=\"Warning\" maxOccurs=\"unbounded\" minOccurs=\"0\"></xsd:element>\n     </xsd:sequence>\n    </xsd:complexType>\n\n    <xsd:complexType name=\"Warning\">\n     <xsd:sequence>\n      <xsd:element name=\"Title\" minOccurs=\"1\" maxOccurs=\"1\">\n       <xsd:simpleType>\n        <xsd:restriction base=\"xsd:string\">\n         <xsd:maxLength value=\"128\"></xsd:maxLength>\n         <xsd:minLength value=\"10\"></xsd:minLength>\n        </xsd:restriction>\n       </xsd:simpleType>\n      </xsd:element>\n      <xsd:element name=\"Description\" minOccurs=\"1\" maxOccurs=\"1\">\n       <xsd:simpleType>\n        <xsd:restriction base=\"xsd:string\">\n         <xsd:minLength value=\"10\"></xsd:minLength>\n         <xsd:maxLength value=\"256\"></xsd:maxLength>\n        </xsd:restriction>\n       </xsd:simpleType>\n      </xsd:element>\n      <xsd:element name=\"LongDescription\" type=\"xsd:string\"\n       minOccurs=\"0\" maxOccurs=\"1\">\n      </xsd:element>\n     </xsd:sequence>\n     <xsd:attribute name=\"Type\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n    </xsd:complexType>\n    \n    <xsd:element name=\"Messages\" type=\"Root\"></xsd:element>\n    \n    <xsd:complexType name=\"Root\">\n     <xsd:sequence>\n      <xsd:element name=\"WarningList\" type=\"WarningList\"></xsd:element>\n     </xsd:sequence>\n    </xsd:complexType>\n</xsd:schema>"
  },
  {
    "path": "huntbugs/src/main/resources/huntbugs/report.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsd:schema elementFormDefault=\"unqualified\"\n  xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n  xmlns=\"https://raw.githubusercontent.com/amaembo/huntbugs/master/huntbugs/src/main/resources/huntbugs\"\n  targetNamespace=\"https://raw.githubusercontent.com/amaembo/huntbugs/master/huntbugs/src/main/resources/huntbugs\">\n\n  <xsd:annotation>\n    <xsd:documentation>HuntBugs warning descriptions</xsd:documentation>\n  </xsd:annotation>\n\n  <xsd:element name=\"HuntBugs\" type=\"Root\"></xsd:element>\n\n  <xsd:complexType name=\"Root\">\n    <xsd:sequence>\n      <xsd:element name=\"ErrorList\" type=\"ErrorList\"\n        minOccurs=\"0\" maxOccurs=\"1\"></xsd:element>\n      <xsd:element name=\"WarningList\" type=\"WarningList\"\n        minOccurs=\"1\" maxOccurs=\"1\"></xsd:element>\n    </xsd:sequence>\n  </xsd:complexType>\n\n  <xsd:complexType name=\"ErrorList\">\n    <xsd:sequence>\n      <xsd:element name=\"Error\" type=\"Error\" maxOccurs=\"unbounded\"\n        minOccurs=\"0\"></xsd:element>\n    </xsd:sequence>\n  </xsd:complexType>\n\n  <xsd:complexType name=\"Error\">\n    <xsd:simpleContent>\n      <xsd:extension base=\"xsd:string\">\n        <xsd:attribute name=\"Detector\" type=\"xsd:string\"></xsd:attribute>\n        <xsd:attribute name=\"Class\" type=\"xsd:string\"></xsd:attribute>\n        <xsd:attribute name=\"Member\" type=\"xsd:string\"></xsd:attribute>\n        <xsd:attribute name=\"Signature\" type=\"xsd:string\"></xsd:attribute>\n        <xsd:attribute name=\"Line\" type=\"LineType\"></xsd:attribute>\n      </xsd:extension>\n    </xsd:simpleContent>\n  </xsd:complexType>\n\n  <xsd:complexType name=\"WarningList\">\n    <xsd:sequence>\n      <xsd:element name=\"Warning\" type=\"Warning\" maxOccurs=\"unbounded\"\n        minOccurs=\"0\"></xsd:element>\n    </xsd:sequence>\n  </xsd:complexType>\n\n  <xsd:complexType name=\"Warning\">\n    <xsd:sequence>\n      <xsd:element name=\"Title\" minOccurs=\"0\" maxOccurs=\"1\"\n        type=\"xsd:string\">\n      </xsd:element>\n      <xsd:element name=\"Description\" minOccurs=\"0\"\n        maxOccurs=\"1\" type=\"xsd:string\">\n      </xsd:element>\n      <xsd:element name=\"LongDescription\" type=\"xsd:string\"\n        minOccurs=\"0\" maxOccurs=\"1\">\n      </xsd:element>\n      <xsd:element name=\"Class\" type=\"Class\" minOccurs=\"1\"\n        maxOccurs=\"1\">\n      </xsd:element>\n      <xsd:element name=\"Method\" type=\"Method\" minOccurs=\"0\"\n        maxOccurs=\"1\">\n      </xsd:element>\n      <xsd:element name=\"Location\" type=\"SourceFileElement\"\n        minOccurs=\"0\" maxOccurs=\"1\">\n      </xsd:element>\n      <xsd:element name=\"AnotherLocation\" type=\"SourceFileElement\"\n        minOccurs=\"0\" maxOccurs=\"unbounded\">\n      </xsd:element>\n      <xsd:element name=\"MemberAnnotation\" type=\"MemberAnnotation\"\n        minOccurs=\"0\" maxOccurs=\"unbounded\">\n      </xsd:element>\n      <xsd:element name=\"TypeAnnotation\" type=\"TypeAnnotation\"\n        minOccurs=\"0\" maxOccurs=\"unbounded\">\n      </xsd:element>\n      <xsd:element name=\"LocationAnnotation\" type=\"LocationAnnotation\"\n        minOccurs=\"0\" maxOccurs=\"unbounded\">\n      </xsd:element>\n      <xsd:element name=\"NumberAnnotation\" type=\"NumberAnnotation\"\n        minOccurs=\"0\" maxOccurs=\"unbounded\">\n      </xsd:element>\n      <xsd:element name=\"Annotation\" type=\"Annotation\"\n        minOccurs=\"0\" maxOccurs=\"unbounded\">\n      </xsd:element>\n    </xsd:sequence>\n    <xsd:attribute name=\"Type\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n    <xsd:attribute name=\"Category\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n    <xsd:attribute name=\"Score\" type=\"Score\" use=\"required\"></xsd:attribute>\n  </xsd:complexType>\n\n  <xsd:complexType name=\"Class\">\n    <xsd:complexContent>\n      <xsd:extension base=\"SourceFileElement\">\n        <xsd:attribute name=\"Name\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n\n\n      </xsd:extension>\n    </xsd:complexContent>\n  </xsd:complexType>\n\n  <xsd:complexType name=\"SourceFileElement\">\n    <xsd:attribute name=\"SourceFile\" type=\"xsd:string\"\n      use=\"optional\">\n    </xsd:attribute>\n    <xsd:attribute name=\"Line\" type=\"LineType\" use=\"optional\" />\n    <xsd:attribute name=\"EndLine\" type=\"LineType\" use=\"optional\"></xsd:attribute>\n  </xsd:complexType>\n\n  <xsd:simpleType name=\"LineType\">\n    <xsd:restriction base=\"xsd:int\">\n      <xsd:minInclusive value=\"0\"></xsd:minInclusive>\n    </xsd:restriction>\n  </xsd:simpleType>\n\n  <xsd:complexType name=\"Method\">\n    <xsd:complexContent>\n      <xsd:extension base=\"SourceFileElement\">\n        <xsd:attribute name=\"Name\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n        <xsd:attribute name=\"Signature\" type=\"xsd:string\"\n          use=\"required\"></xsd:attribute>\n      </xsd:extension>\n    </xsd:complexContent>\n  </xsd:complexType>\n\n\n  <xsd:complexType name=\"Annotation\">\n    <xsd:simpleContent>\n      <xsd:extension base=\"xsd:string\">\n        <xsd:attribute name=\"Role\" type=\"AnnotationRole\"></xsd:attribute>\n      </xsd:extension>\n    </xsd:simpleContent>\n  </xsd:complexType>\n\n  <xsd:simpleType name=\"Score\">\n    <xsd:restriction base=\"xsd:int\">\n      <xsd:minInclusive value=\"0\"></xsd:minInclusive>\n      <xsd:maxInclusive value=\"100\"></xsd:maxInclusive>\n    </xsd:restriction>\n  </xsd:simpleType>\n\n    <xsd:complexType name=\"MemberAnnotation\">\n       <xsd:attribute name=\"Role\" type=\"AnnotationRole\"></xsd:attribute>\n       <xsd:attribute name=\"Type\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n       <xsd:attribute name=\"Name\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n       <xsd:attribute name=\"Signature\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n    </xsd:complexType>\n\n    <xsd:complexType name=\"NumberAnnotation\">\n       <xsd:attribute name=\"Role\" type=\"AnnotationRole\"></xsd:attribute>\n       <xsd:attribute name=\"Type\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n       <xsd:attribute name=\"Value\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n    </xsd:complexType>\n\n    <xsd:complexType name=\"TypeAnnotation\">\n       <xsd:attribute name=\"Role\" type=\"AnnotationRole\"></xsd:attribute>\n       <xsd:attribute name=\"Name\" type=\"xsd:string\" use=\"required\"></xsd:attribute>\n    </xsd:complexType>\n\n    <xsd:complexType name=\"LocationAnnotation\">\n       <xsd:attribute name=\"Role\" type=\"AnnotationRole\"></xsd:attribute>\n       <xsd:attribute name=\"Line\" type=\"LineType\" use=\"required\"></xsd:attribute>\n    </xsd:complexType>\n\n    <xsd:simpleType name=\"AnnotationRole\">\n      <xsd:restriction base=\"xsd:string\">\n        <xsd:minLength value=\"2\"></xsd:minLength>\n        <xsd:maxLength value=\"32\"></xsd:maxLength>\n      </xsd:restriction>\n    </xsd:simpleType>\n\n</xsd:schema>"
  },
  {
    "path": "huntbugs/src/main/resources/huntbugs/report.xsl",
    "content": "<?xml version=\"1.0\"?>\n<xsl:stylesheet version=\"1.0\"\n  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n  xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n  exclude-result-prefixes=\"xlink\">\n\n  <xsl:output doctype-public=\"-//W3C//DTD HTML 4.01 Transitional//EN\"\n    doctype-system=\"http://www.w3.org/TR/html4/loose.dtd\"\n    encoding=\"UTF-8\"/>\n \n  <xsl:template match=\"/\">\n    <html>\n      <!-- HTML header -->\n      <xsl:call-template name=\"make-html-header\"/>\n      <body>\n        <h1>HuntBugs report</h1>\n        <xsl:apply-templates/>\n        <xsl:call-template name=\"make-scripts\"/>\n      </body>\n    </html>\n  </xsl:template>\n  \n  <xsl:template match=\"HuntBugs\">\n     <div>\n       <xsl:if test=\"count(WarningList/Warning[@Status!='fixed'])>0\">\n         <div class=\"Tab\" data-target=\"warnings-all\">All warnings (<xsl:value-of select=\"count(WarningList/Warning[@Status!='fixed'])\"/>)</div>\n       </xsl:if>\n       <xsl:if test=\"count(WarningList/Warning[@Status='added'])>0\">\n         <div class=\"Tab\" data-target=\"warnings-added\">Added (<xsl:value-of select=\"count(WarningList/Warning[@Status='added'])\"/>)</div>\n       </xsl:if>\n       <xsl:if test=\"count(WarningList/Warning[@Status='changed'])>0\">\n         <div class=\"Tab\" data-target=\"warnings-changed\">Changed (<xsl:value-of select=\"count(WarningList/Warning[@Status='changed'])\"/>)</div>\n       </xsl:if>\n       <xsl:if test=\"count(WarningList/Warning[@Status='score_raised'])>0\">\n         <div class=\"Tab\" data-target=\"warnings-raised\">Score raised (<xsl:value-of select=\"count(WarningList/Warning[@Status='score_raised'])\"/>)</div>\n       </xsl:if>\n       <xsl:if test=\"count(WarningList/Warning[@Status='score_lowered'])>0\">\n         <div class=\"Tab\" data-target=\"warnings-lowered\">Score lowered (<xsl:value-of select=\"count(WarningList/Warning[@Status='score_lowered'])\"/>)</div>\n       </xsl:if>\n       <xsl:if test=\"count(WarningList/Warning[@Status='fixed'])>0\">\n         <div class=\"Tab\" data-target=\"warnings-fixed\">Fixed (<xsl:value-of select=\"count(WarningList/Warning[@Status='fixed'])\"/>)</div>\n       </xsl:if>\n       <xsl:if test=\"count(ErrorList/Error)>0\">\n         <div class=\"Tab\" data-target=\"errors\">Errors (<xsl:value-of select=\"count(ErrorList/Error)\"/>)</div>\n       </xsl:if>\n     </div>\n     <xsl:apply-templates/>\n  </xsl:template>\n\n  <xsl:template name=\"make-scripts\">\n    <script type=\"text/javascript\"><![CDATA[\n        function toggle(e) {\n          if(/ Hidden$/.test(e.className)) {\n            e.className = e.className.substring(0, e.className.length-' Hidden'.length);\n          } else {\n            e.className += ' Hidden';\n          }\n        }\n\n        function updateCount(tabContent) {\n          var warnings = tabContent.getElementsByClassName(\"WarningsBody\")[0].children;\n          var total = 0, shown = 0;\n          for(var i=0; i<warnings.length; i+=2) {\n            total++;\n            if(!/ Hidden$/.test(warnings[i].className))\n              shown++;\n          }\n          tabContent.getElementsByClassName(\"WarningCount\")[0].innerText = shown+\"/\"+total;\n        }\n        \n        var tabRecords = [];\n        \n        function activateTab(tab) {\n          for(var i=0; i<tabRecords.length; i++) {\n            if(tabRecords[i][0] == tab) {\n              tabRecords[i][0].className = \"Tab\";\n              tabRecords[i][1].className = \"TabContent Active\";\n            } else {\n              tabRecords[i][0].className = \"Tab Inactive\";\n              tabRecords[i][1].className = \"TabContent\";\n            }\n          }\n        }\n        \n        function initTabs() {\n          var tabs = document.getElementsByClassName(\"Tab\");\n          \n          for(var i=0; i<tabs.length; i++) {\n            var tab = tabs[i];\n            var tabContent = document.getElementById(tab.getAttribute(\"data-target\"));\n            tabRecords.push([tab, tabContent]);\n            (function(tab) { \n              tabs[i].addEventListener(\"click\", function() {activateTab(tab);});\n            })(tab);\n            initTabContent(tabContent);\n          }\n          if(tabs.length > 0) {\n            activateTab(tabs[0]);\n          }\n        }\n        \n        function initTabContent(tabContent) {\n          var rows = tabContent.getElementsByClassName(\"WarningRow\");\n          for(var i=0; i<rows.length; i++) {\n            var btns = rows[i].getElementsByClassName(\"hideWarning\");\n            if(btns.length == 0)\n              continue;\n            (function(clsName, btn) {\n              btn.addEventListener(\"click\", function() {\n                var toHide = tabContent.getElementsByClassName(\"Warning-\"+clsName);\n                for(var j=0; j<toHide.length; j++) {\n                  toggle(toHide[j]);\n                }\n                updateCount(tabContent);\n              });\n            })(/Warning-(\\w+)/.exec(rows[i].className)[1], btns[0]);\n          }\n        }\n\n        initTabs();\n    ]]></script>\n  </xsl:template>\n  \n  <xsl:template name=\"make-html-header\">\n    <head>\n      <title>\n        HuntBugs Report\n      </title>\n      <style>\n      body {\n        font-family: verdana, helvetica, sans-serif;\n      }\n\n      br {\n        margin: 1em;\n      }\n      \n      code {\n        font-size: 140%;\n      }\n      \n      span.WarningType {\n        font-size: 70%;\n        color: gray;\n      }\n      \n      code.Member {\n        border-bottom: 1px gray dotted;\n      }\n      \n      .AnotherLocation {\n        color: gray;\n      }\n      \n      table.Warnings, table.Errors {\n        border-collapse: collapse;\n        margin: 3pt;\n        width: 100%;\n      }\n      \n      table.Warnings, table.Warnings > tbody > tr > td {\n        border: 1px solid blue;\n        padding: 3pt;\n      }\n\n      table.Errors thead {\n        background-color: red;\n        color: white;\n      }\n      \n      table.Errors, table.Errors > tbody > tr > td {\n        border: 1px solid red;\n        padding: 3pt;\n      }\n      \n      table.Errors > tbody > tr > td {\n        vertical-align: top;\n      }\n      \n      .Title {\n        font-weight: bold;\n      }\n      \n      td.Description {\n        background-color: yellow;\n        height: 10pt;\n        vertical-align: top;\n      }\n      \n      table.Properties th {\n        text-align: right;\n        font-weight: normal;\n        font-size: 80%;\n        color: #444;\n      }\n      \n      .hideWarning {\n        cursor: pointer;\n        color: #55F;\n        text-decoration: underline;\n      }\n      \n      .WarningRow.Hidden {\n        display: none;\n      }\n      \n      .TabContent {\n        display: none;\n      }\n      \n      .TabContent.Active {\n        display: block;\n        border: 1px solid #DDD;\n        padding: 3pt;\n        clear: both;\n      }\n      \n      .Tab {\n        float: left;\n        padding: 1em;\n        background-color: #DDD;\n        margin-right: 5pt;\n      }\n      \n      .Tab.Inactive {\n        background-color: #BBB;\n        cursor: pointer;\n      }\n      </style>\n    </head>\n  </xsl:template>\n\n  <xsl:template match=\"ErrorList\">\n    <div class=\"TabContent\" id=\"errors\">\n      <table class=\"Errors\"><thead><tr><th colspan=\"2\">Errors (<xsl:value-of select=\"count(Error)\"/>)</th></tr></thead>\n        <tbody class=\"ErrorsBody Hidden\">\n          <xsl:apply-templates/>\n        </tbody>\n      </table>\n    </div>\n  </xsl:template>\n\n  <xsl:template match=\"Error\">\n    <tr>\n        <td>\n            <table class=\"Properties\">\n              <xsl:choose><xsl:when test=\"@Class\"><tr><th>Class:</th><td><xsl:value-of select=\"@Class\"/></td></tr></xsl:when></xsl:choose>\n              <xsl:choose><xsl:when test=\"@Member\"><tr><th>Member:</th><td><xsl:value-of select=\"@Member\"/></td></tr></xsl:when></xsl:choose>\n              <xsl:choose><xsl:when test=\"@Detector\"><tr><th>Detector:</th><td><xsl:value-of select=\"@Detector\"/></td></tr></xsl:when></xsl:choose>\n            </table>\n        </td>\n        <td><pre><xsl:value-of select=\".\"/></pre>\n        </td>\n    </tr>\n  </xsl:template>\n  \n  <xsl:template match=\"WarningList\">\n    <div id=\"warnings-all\" class=\"TabContent\">\n    <table class=\"Warnings\"><thead><tr><th colspan=\"2\">All warnings (<span class=\"WarningCount\"><xsl:value-of select=\"count(Warning[@Status!='fixed'])\"/></span>)</th></tr></thead>\n      <tbody class=\"WarningsBody\">\n        <xsl:apply-templates select=\"Warning[@Status!='fixed']\"/>\n      </tbody>\n    </table>\n    </div>\n    <div id=\"warnings-added\" class=\"TabContent\">\n    <table class=\"Warnings\"><thead><tr><th colspan=\"2\">Added (<span class=\"WarningCount\"><xsl:value-of select=\"count(Warning[@Status='added'])\"/></span>)</th></tr></thead>\n      <tbody class=\"WarningsBody\">\n        <xsl:apply-templates select=\"Warning[@Status='added']\"/>\n      </tbody>\n    </table>\n    </div>\n    <div id=\"warnings-changed\" class=\"TabContent\">\n    <table class=\"Warnings\"><thead><tr><th colspan=\"2\">Changed (<span class=\"WarningCount\"><xsl:value-of select=\"count(Warning[@Status='changed'])\"/></span>)</th></tr></thead>\n      <tbody class=\"WarningsBody\">\n        <xsl:apply-templates select=\"Warning[@Status='changed']\"/>\n      </tbody>\n    </table>\n    </div>\n    <div id=\"warnings-raised\" class=\"TabContent\">\n    <table class=\"Warnings\"><thead><tr><th colspan=\"2\">Score raised (<span class=\"WarningCount\"><xsl:value-of select=\"count(Warning[@Status='score_raised'])\"/></span>)</th></tr></thead>\n      <tbody class=\"WarningsBody\">\n        <xsl:apply-templates select=\"Warning[@Status='score_raised']\"/>\n      </tbody>\n    </table>\n    </div>\n    <div id=\"warnings-lowered\" class=\"TabContent\">\n    <table class=\"Warnings\"><thead><tr><th colspan=\"2\">Score lowered (<span class=\"WarningCount\"><xsl:value-of select=\"count(Warning[@Status='score_lowered'])\"/></span>)</th></tr></thead>\n      <tbody class=\"WarningsBody\">\n        <xsl:apply-templates select=\"Warning[@Status='score_lowered']\"/>\n      </tbody>\n    </table>\n    </div>\n    <div id=\"warnings-fixed\" class=\"TabContent\">\n    <table class=\"Warnings\"><thead><tr><th colspan=\"2\">Fixed (<span class=\"WarningCount\"><xsl:value-of select=\"count(Warning[@Status='fixed'])\"/></span>)</th></tr></thead>\n      <tbody class=\"WarningsBody\">\n        <xsl:apply-templates select=\"Warning[@Status='fixed']\"/>\n      </tbody>\n    </table>\n    </div>\n  </xsl:template>\n\n  <xsl:template match=\"Warning\">\n    <tr>\n        <xsl:attribute name=\"class\">WarningRow Warning-<xsl:value-of select=\"@Type\"/></xsl:attribute>\n        <td rowspan=\"2\">\n            <div class=\"Title\"><xsl:value-of select=\"Title\"/><br/><span class=\"WarningType\">(<xsl:value-of select=\"@Type\"/> [<span class=\"hideWarning\" title=\"Hide this type of warnings\">x</span>])</span></div>\n            <table class=\"Properties\">\n            <tr><th>Category:</th><td><xsl:value-of select=\"@Category\"/></td></tr>\n            <tr><th>Score:</th><td><xsl:value-of select=\"@Score\"/></td></tr>\n            <xsl:apply-templates select=\"Location\"/>\n            <xsl:apply-templates select=\"Class\"/>\n            <xsl:apply-templates select=\"Method\"/>\n            <xsl:apply-templates select=\"Field\"/>\n            <xsl:apply-templates select=\"Annotation[@Role='VARIABLE']\"/>\n            <xsl:apply-templates select=\"LocationAnnotation[@Role='DEAD_CODE_LOCATION']\"/>\n            </table>\n        </td>\n        <td class=\"Description\">\n            <div class=\"Description\"><xsl:value-of select=\"Description\"/></div>\n        </td>\n    </tr>\n    <tr>\n        <xsl:attribute name=\"class\">WarningRow Warning-<xsl:value-of select=\"@Type\"/></xsl:attribute>\n        <td>\n            <div class=\"LongDescription\"><xsl:value-of select=\"LongDescription/text()\" disable-output-escaping=\"yes\"/></div>\n        </td>\n    </tr>\n  </xsl:template>\n\n  <xsl:template match=\"Location\">\n    <tr><th>Location:</th><td><xsl:value-of select=\"@SourceFile\"/>:<xsl:value-of select=\"@Line\"/><xsl:apply-templates select=\"../AnotherLocation\"/></td></tr>\n  </xsl:template>\n\n  <xsl:template match=\"AnotherLocation\">\n    <span class=\"AnotherLocation\">; <xsl:value-of select=\"@Line\"/></span>\n  </xsl:template>\n\n  <xsl:template match=\"Class\">\n    <tr><th>Class:</th><td><xsl:value-of select=\"@Name\"/></td></tr>\n  </xsl:template>\n\n  <xsl:template match=\"Method\">\n    <tr><th>Method:</th><td><xsl:value-of select=\"@Name\"/></td></tr>\n  </xsl:template>\n\n  <xsl:template match=\"Field\">\n    <tr><th>Field:</th><td><xsl:value-of select=\"@Name\"/></td></tr>\n  </xsl:template>\n\n  <xsl:template match=\"Annotation[@Role='VARIABLE']\">\n    <tr><th>Variable:</th><td><xsl:value-of select=\"text()\"/></td></tr>\n  </xsl:template>\n\n  <xsl:template match=\"LocationAnnotation[@Role='DEAD_CODE_LOCATION']\">\n    <tr><th>Dead code at:</th><td><xsl:value-of select=\"@Line\"/></td></tr>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/AsserterTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs;\n\nimport static org.junit.Assert.*;\n\nimport one.util.huntbugs.analysis.AnalysisOptions;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.repo.Repository;\n\nimport org.junit.Test;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class AsserterTest {\n    @Test\n    public void test() throws Exception {\n        Context ctx = new Context(Repository.createSelfRepository(), new AnalysisOptions());\n        ctx.analyzePackage(\"one/util/huntbugs/asserter\");\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule: AssertNoWarning(RoughConstantValue)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule: AssertNoWarning(Rough*)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule: AssertNoWarning(BadNameOfField)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule: AssertNoWarning(UncalledPrivateMethod)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule: AssertNoWarning(ParameterOverwritten)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule is not satisfied: AssertWarning(AAA; score = 0..100)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule is not satisfied: AssertWarning(BBB; score = 0..100)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule is not satisfied: AssertWarning(CCC; score = 0..100)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule is not satisfied: AssertWarning(ParameterOverwritte*; score = 0..100)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule is not satisfied: AssertWarning(ParameterOverwritt*; score = 0..100)\")));\n        assertTrue(ctx.errors().anyMatch(em -> em.toString().contains(\"rule is not satisfied: AssertWarning(BadName*; score = 0..100)\")));\n        assertEquals(11, ctx.getErrorCount());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/DataTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs;\n\nimport one.util.huntbugs.spi.DataTests;\nimport org.junit.Test;\n\n/**\n * @author Tagir Valeev\n */\npublic class DataTest {\n\n    @Test\n    public void test() throws Exception {\n        DataTests.test(\"one/util/huntbugs/testdata\");\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/FilteredRepositoryTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs;\n\nimport static org.junit.Assert.*;\n\nimport java.util.stream.Collectors;\n\nimport one.util.huntbugs.analysis.AnalysisOptions;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.repo.FilteredRepository;\nimport one.util.huntbugs.repo.Repository;\n\nimport org.junit.Test;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class FilteredRepositoryTest {\n    @Test\n    public void filteredRepoTest() {\n        Repository parent = Repository.createSelfRepository();\n        Repository repo = new FilteredRepository(parent, cn -> cn.endsWith(\"/TestSyncGetClass\"));\n        Context ctx = new Context(repo, new AnalysisOptions());\n        ctx.analyzePackage(\"one/util/huntbugs/testdata\");\n        assertEquals(\"\", ctx.errors().map(Object::toString).collect(Collectors.joining()));\n        assertTrue(ctx.warnings().count() > 0);\n        ctx.warnings().allMatch(w -> w.getType().getName().equals(\"SyncOnGetClass\"));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/MessagesTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\n\nimport one.util.huntbugs.warning.Formatter;\nimport one.util.huntbugs.warning.Messages;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningType;\nimport one.util.huntbugs.warning.Messages.Message;\nimport one.util.huntbugs.warning.Roles;\n\nimport org.junit.Test;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class MessagesTest {\n    @Test\n    public void testMessages() {\n        Messages m = Messages.load();\n        Message msg = m.getMessagesForType(\"RoughConstantValue\");\n        assertEquals(\"Rough value of known constant is used\", msg.getTitle());\n        assertEquals(\"Constant $NUMBER$ should be replaced with $REPLACEMENT$\", msg.getDescription());\n    }\n\n    @Test\n    public void testFormatter() {\n        Formatter f = new Formatter();\n        WarningType type = new WarningType(\"BadPractice\", \"RoughConstantValue\", 60);\n        Warning w = new Warning(type, 0, Arrays.asList(Roles.NUMBER.create(3.1415), Roles.REPLACEMENT_STRING.create(\n            \"Math.PI\")));\n        assertEquals(\"Rough value of known constant is used\", f.getTitle(w));\n        assertEquals(\"Constant 3.1415 should be replaced with Math.PI\", f.getDescription(w));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/OptionsTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs;\n\nimport static org.junit.Assert.*;\nimport one.util.huntbugs.analysis.AnalysisOptions;\n\nimport org.junit.Test;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class OptionsTest {\n    @Test\n    public void testAnalysisOptions() {\n        AnalysisOptions opt = new AnalysisOptions();\n        assertTrue(opt.addBootClassPath);\n        opt.set(\"minScore\", \"40\");\n        opt.set(\"addBootClassPath\", \"False\");\n        assertEquals(40, opt.minScore);\n        assertFalse(opt.addBootClassPath);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/ReportsTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.junit.Test;\n\nimport one.util.huntbugs.analysis.AnalysisOptions;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.ErrorMessage;\nimport one.util.huntbugs.analysis.HuntBugsResult;\nimport one.util.huntbugs.output.Reports;\nimport one.util.huntbugs.repo.Repository;\nimport one.util.huntbugs.warning.Roles;\nimport one.util.huntbugs.warning.Warning;\nimport one.util.huntbugs.warning.WarningStatus;\nimport one.util.huntbugs.warning.WarningType;\nimport one.util.huntbugs.warning.Role.StringRole;\n\n/**\n * @author lan\n *\n */\npublic class ReportsTest {\n    @Test(expected = IllegalArgumentException.class)\n    public void testMergeZero() {\n        Reports.merge(Collections.emptyList());\n    }\n\n    @Test\n    public void testMerge() {\n        Context ctx = new Context(Repository.createNullRepository(), new AnalysisOptions());\n        ctx.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type\"))));\n        ctx.addError(new ErrorMessage(\"detector\", \"cls\", \"member\", \"desc\", -1, \"Error\"));\n\n        Context ctx2 = new Context(Repository.createNullRepository(), new AnalysisOptions());\n        ctx2.addWarning(new Warning(ctx.getWarningType(\"BadNameOfField\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type2\"))));\n        ctx2.addError(new ErrorMessage(\"detector2\", \"cls\", \"member\", \"desc\", -1, \"Error\"));\n\n        HuntBugsResult result = Reports.merge(Arrays.asList(ctx, ctx2));\n        assertEquals(2, result.errors().count());\n        assertEquals(2, result.warnings().count());\n\n        assertEquals(Arrays.asList(\"detector\", \"detector2\"), result.errors().map(ErrorMessage::getDetector).collect(\n            Collectors.toList()));\n        assertEquals(Arrays.asList(\"RoughConstantValue\", \"BadNameOfField\"), result.warnings().map(Warning::getType)\n                .map(WarningType::getName).collect(Collectors.toList()));\n    }\n\n    @Test\n    public void testDiff() {\n        Context ctx = new Context(Repository.createNullRepository(), new AnalysisOptions());\n        ctx.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type\"))));\n        ctx.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type2\"))));\n        ctx.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type5\"), Roles.NUMBER.create(1.23), Roles.OPERATION.create(\"==\"))));\n        ctx.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type5\"), Roles.NUMBER.create(1.25))));\n        ctx.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type4\"), Roles.NUMBER.create(1.23))));\n        ctx.addError(new ErrorMessage(\"detector\", \"cls\", \"member\", \"desc\", -1, \"Error\"));\n\n        Context ctx2 = new Context(Repository.createNullRepository(), new AnalysisOptions());\n        ctx2.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 5, Arrays.asList(Roles.TYPE\n                .create(\"test/type2\"))));\n        ctx2.addWarning(new Warning(ctx.getWarningType(\"BadNameOfField\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type3\"))));\n        ctx2.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type5\"), Roles.NUMBER.create(1.23), StringRole.forName(\"OPERATION\").create(\"==\"))));\n        ctx2.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type5\"), Roles.NUMBER.create(1.25))));\n        ctx2.addWarning(new Warning(ctx.getWarningType(\"RoughConstantValue\"), 0, Arrays.asList(Roles.TYPE\n                .create(\"test/type4\"), Roles.NUMBER.create(1.24))));\n        ctx2.addError(new ErrorMessage(\"detector2\", \"cls\", \"member\", \"desc\", -1, \"Error\"));\n\n        HuntBugsResult result = Reports.diff(ctx, ctx2);\n        assertEquals(1, result.errors().count());\n        assertEquals(Optional.of(\"detector2\"), result.errors().map(ErrorMessage::getDetector).findFirst());\n\n        assertEquals(6, result.warnings().count());\n        assertEquals(Optional.of(WarningStatus.ADDED), result.warnings().filter(\n            w -> w.getType().getName().equals(\"BadNameOfField\")).map(Warning::getStatus).findFirst());\n        assertEquals(Optional.of(WarningStatus.FIXED), result.warnings().filter(\n            w -> w.getClassName().equals(\"test.type\")).map(Warning::getStatus).findFirst());\n        assertTrue(result.warnings().filter(w -> w.getClassName().equals(\"test.type5\")).map(Warning::getStatus)\n                .allMatch(WarningStatus.DEFAULT::equals));\n        assertEquals(Optional.of(WarningStatus.CHANGED), result.warnings().filter(\n            w -> w.getClassName().equals(\"test.type4\")).map(Warning::getStatus).findFirst());\n        assertEquals(Optional.of(WarningStatus.SCORE_LOWERED), result.warnings().filter(\n            w -> w.getClassName().equals(\"test.type2\") && w.getType().getName().equals(\"RoughConstantValue\")).map(\n            Warning::getStatus).findFirst());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/RuleTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\n\nimport one.util.huntbugs.analysis.AnalysisOptions;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.repo.Repository;\nimport one.util.huntbugs.warning.rule.CategoryRule;\nimport one.util.huntbugs.warning.rule.CompositeRule;\nimport one.util.huntbugs.warning.rule.RegexRule;\n\nimport org.junit.Test;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class RuleTest {\n    @Test\n    public void testRules() {\n        AnalysisOptions options = new AnalysisOptions();\n        Context ctx = new Context(Repository.createNullRepository(), options);\n        assertEquals(60, ctx.getWarningType(\"RoughConstantValue\").getMaxScore());\n        options.setRule(new CategoryRule(\"BadPractice\", -10));\n        ctx = new Context(Repository.createNullRepository(), options);\n        assertEquals(50, ctx.getWarningType(\"RoughConstantValue\").getMaxScore());\n        options.setRule(new CompositeRule(Arrays.asList(options.getRule(), new RegexRule(\"Rough.+\", -20))));\n        ctx = new Context(Repository.createNullRepository(), options);\n        assertEquals(30, ctx.getWarningType(\"RoughConstantValue\").getMaxScore());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/asserter/TestAsserter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.asserter;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\n@AssertWarning(\"BBB\")\n@AssertNoWarning(\"RoughConstantValue\")\npublic class TestAsserter {\n    @AssertWarning(\"CCC\")\n    @AssertNoWarning(\"BadNameOfField\")\n    int TheField = 123;\n\n    @AssertWarning(\"BadName*\")\n    @SuppressMyWarning(\"BadNameOfField\")\n    int TheFieldSuppressed = 123;\n    \n    @AssertWarning(\"AAA\")\n    @AssertNoWarning(\"Rough*\")\n    public double test() {\n        return 3.1415*TheField;\n    }\n    \n    @AssertWarning(\"ParameterOverwritte*\")\n    @SuppressMyWarning(\"Param*\")\n    public double testSuppress(int x) {\n        x = 10;\n        return x*2;\n    }\n    \n    @AssertWarning(\"ParameterOverwritt*\")\n    public double testSuppressParam(@SuppressMyWarning(\"all\") int x) {\n        x = 10;\n        return x*2;\n    }\n    \n    @AssertNoWarning(\"UncalledPrivateMethod\")\n    private void uncalled() {\n        System.out.println(\"Uncalled\");\n    }\n    \n    public void testLocalClass() {\n        class X {\n            @AssertNoWarning(\"ParameterOverwritten\")\n            public void print(int x) {\n                x = 10;\n                System.out.println(x);\n            }\n        }\n        new X().print(5);\n    }\n    \n    @interface SuppressMyWarning {\n        String value();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/registry/DetectorRegistryTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.registry;\n\nimport one.util.huntbugs.analysis.AnalysisOptions;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.repo.Repository;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author Mihails Volkovs\n */\npublic class DetectorRegistryTest {\n\n    private Context context;\n\n    private DetectorRegistry detectorRegistry;\n\n    @Before\n    public void setUp() {\n        context = new Context(Repository.createNullRepository(), new AnalysisOptions());\n        detectorRegistry = new DetectorRegistry(context);\n    }\n\n    @Test\n    public void addDetector() {\n        final long WARNINGS = getWarnings();\n        assertTrue(detectorRegistry.addDetector(TestDetector.class));\n        assertEquals(WARNINGS + 2, getWarnings());\n    }\n\n    @Test\n    public void addFakeDetector() {\n        final long WARNINGS = getWarnings();\n        assertFalse(detectorRegistry.addDetector(DetectorRegistryTest.class));\n        assertEquals(WARNINGS, getWarnings());\n    }\n\n    private long getWarnings() {\n        return context.getStat(\"WarningTypes.Total\");\n    }\n\n    @WarningDefinition(category=\"DetectorRegistryTest\", name=\"DetectorRegistryTest\", maxScore=80)\n    @WarningDefinition(category=\"DetectorRegistryTest\", name=\"DetectorRegistryTest\", maxScore=80)\n    private static class TestDetector {\n\n    }\n\n}"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestAbandonedStream.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.List;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestAbandonedStream {\n    @AssertWarning(\"AbandonedStream\")\n    public void testSimple(List<String> list) {\n        list.stream().map(String::trim);\n    }\n    \n    @AssertNoWarning(\"AbandonedStream\")\n    @AssertWarning(\"StreamMethodMayNotReturnItself\")\n    public Stream<String> testClose(List<String> list) {\n        Stream<String> stream = list.stream();\n        stream.onClose(() -> System.out.println(\"Closed!\"));\n        return stream;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testSimpleOk(List<String> list) {\n        list.stream().map(String::trim).forEach(System.out::println);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public Stream<String> testSimpleReturn(List<String> list) {\n        return list.stream().map(String::trim);\n    }\n\n    @AssertWarning(\"AbandonedStream\")\n    public void testVar(List<String> list) {\n        Stream<String> stream = list.stream();\n        stream = stream.map(String::trim);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testVarOk(List<String> list) {\n        Stream<String> stream = list.stream();\n        stream = stream.map(String::trim);\n        stream.forEach(System.out::println);\n    }\n    \n    @AssertWarning(\"AbandonedStream\")\n    public void testIf(List<String> list, boolean b) {\n        Stream<String> stream = list.stream();\n        if(b) {\n            stream = stream.map(String::trim);\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testIfOk(List<String> list, boolean b) {\n        Stream<String> stream = list.stream();\n        if(b) {\n            stream = stream.map(String::trim);\n        }\n        stream.forEach(System.out::println);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testTernaryOk(List<String> list, boolean b) {\n        Stream<String> stream = b ? list.stream() : list.stream().map(String::trim);\n        stream.forEach(System.out::println);\n    }\n    \n    @AssertWarning(\"AbandonedStream\")\n    public void testToPrimitive(List<String> list) {\n        list.stream().mapToInt(String::length);\n    }\n    \n    @AssertWarning(\"AbandonedStream\")\n    public void testPrimitive(int[] data) {\n        IntStream.of(data).mapToObj(String::valueOf);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestAppendObjectOutputStream.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.BufferedOutputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\nimport java.io.OutputStream;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestAppendObjectOutputStream {\n    @AssertWarning(\"AppendObjectOutputStream\")\n    public ObjectOutputStream createStream() throws IOException {\n        return new ObjectOutputStream(new FileOutputStream(\"/tmp/file\", true));\n    }\n\n    @AssertWarning(\"AppendObjectOutputStream\")\n    public ObjectOutputStream createStreamComplex() throws IOException {\n        OutputStream out = new FileOutputStream(\"/tmp/file\", true);\n        out = new BufferedOutputStream(out);\n        return new ObjectOutputStream(out);\n    }\n\n    @AssertWarning(\"AppendObjectOutputStream\")\n    public ObjectOutputStream createStreamConditional(boolean buffered) throws IOException {\n        OutputStream out = new FileOutputStream(\"/tmp/file\", true);\n        if(buffered)\n            out = new BufferedOutputStream(out);\n        return new ObjectOutputStream(out);\n    }\n    \n    @AssertNoWarning(\"AppendObjectOutputStream\")\n    public ObjectOutputStream createStreamNoAppend() throws IOException {\n        return new ObjectOutputStream(new FileOutputStream(\"/tmp/file\", false));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestArrayRangeCheck.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Arrays;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestArrayRangeCheck {\n    private static final int size = 4;\n    \n    @AssertWarning(\"ArrayIndexNegative\")\n    public int get(int[] arr) {\n        int idx = -1;\n        return arr[idx];\n    }\n\n    @AssertWarning(\"ArrayIndexOutOfRange\")\n    public void test() {\n        int[] arr = {1,2,3};\n        arr[3] = 4;\n        System.out.println(Arrays.toString(arr));\n    }\n\n    @AssertWarning(\"ArrayIndexOutOfRange\")\n    public void testArrayBoolean(boolean b) {\n        int[] arr = {1,2,3};\n        if(b)\n            arr = new int[4];\n        arr[4] = 4;\n        System.out.println(Arrays.toString(arr));\n    }\n\n    @AssertWarning(\"ArrayIndexOutOfRange\")\n    public int[] getValueLocal(boolean flag) {\n        int[] array = new int[5];\n        if(flag) {\n            array = new int[10];\n            array[6] = 1;\n            return array;\n        } else {\n            array[6] = 2;\n            return array;\n        }\n    }\n\n    @AssertWarning(\"ArrayIndexOutOfRange\")\n    public void testArrayClone(boolean b) {\n        int[] arr = {1,2,3};\n        int[] barr = arr.clone();\n        barr[4] = 4;\n        System.out.println(Arrays.toString(barr));\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testOk() {\n        int[] arr = {1,2,3};\n        arr[2] = 4;\n        System.out.println(Arrays.toString(arr));\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testArrayCopyOk() {\n        int[] a = new int[4];\n        int[] b = new int[5];\n        System.arraycopy(a, 0, b, 1, 4);\n        System.out.println(Arrays.toString(b));\n    }\n    \n    @AssertWarning(\"ArrayLengthOutOfRange\")\n    public void testArrayCopy() {\n        int[] a = new int[4];\n        int[] b = new int[5];\n        System.arraycopy(a, 1, b, 0, 4);\n        System.out.println(Arrays.toString(b));\n    }\n    \n    @AssertWarning(\"ArrayLengthOutOfRange\")\n    public void testArrayCopyLength() {\n        int[] a = new int[4];\n        int[] b = new int[5];\n        System.arraycopy(a, 1, b, 0, a.length);\n        System.out.println(Arrays.toString(b));\n    }\n    \n    @AssertWarning(\"ArrayOffsetOutOfRange\")\n    public void testArrayCopyIdx() {\n        int[] a = new int[4];\n        int[] b = new int[5];\n        System.arraycopy(a, 5, b, 0, 4);\n        System.out.println(Arrays.toString(b));\n    }\n    \n    @AssertWarning(\"ArrayIndexNegative\")\n    public void testArrayCopyNegative() {\n        int[] a = new int[4];\n        int[] b = new int[5];\n        System.arraycopy(a, 0, b, -1, 1);\n        System.out.println(Arrays.toString(b));\n    }\n\n    @AssertNoWarning(\"Array*\")\n    public void testArrayCopyCatch() {\n        int[] a = new int[4];\n        int[] b = new int[5];\n        try {\n            a[-1] = 2;\n            b[10] = 3;\n            System.arraycopy(a, -1, b, -1, 1000);\n        } catch (IndexOutOfBoundsException e) {\n            e.printStackTrace();\n        }\n        System.out.println(Arrays.toString(b));\n    }\n\n    public class SubArrayCheck {\n        String[] data;\n        \n        SubArrayCheck(String s) {\n            this();\n            System.out.println(s);\n        }\n\n        SubArrayCheck() {\n            data = new String[size];\n        }\n        \n        @AssertWarning(\"ArrayIndexOutOfRange\")\n        public void set(String a) {\n            data[size] = a;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestAtomicConcurrent.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestAtomicConcurrent {\n    Map<String, Integer> chm = new ConcurrentHashMap<>();\n    Map<String, Integer[]> chm2;\n    \n    public TestAtomicConcurrent() {\n        chm2 = new ConcurrentHashMap<>();\n        new ConcurrentHashMap<>();\n        System.out.println(\"test\");\n    }\n    \n    @AssertWarning(value=\"NonAtomicOperationOnConcurrentMap\", minScore=40, maxScore=60)\n    public void testAtomic(String str) {\n        if(!chm.containsKey(str)) {\n            chm.put(str, 1);\n        } else {\n            chm.put(str, 0);\n        }\n    }\n\n    @AssertWarning(value=\"NonAtomicOperationOnConcurrentMap\", minScore=61)\n    public void testAtomicArray(String str) {\n        if(!chm2.containsKey(str)) {\n            chm2.put(str, new Integer[1]);\n        } else {\n            chm2.put(str, new Integer[2]);\n        }\n    }\n    \n    @AssertWarning(\"NonAtomicOperationOnConcurrentMap\")\n    public void testAtomic2(String str) {\n        Integer oldVal = chm.get(str);\n        if(oldVal == null) {\n            chm.put(str, 1);\n        } else {\n            chm.put(str, 0);\n        }\n    }\n    \n    @AssertWarning(\"NonAtomicOperationOnConcurrentMap\")\n    public void testAtomicUpdate(String str) {\n        chm.put(str, chm.get(str) + 1);\n    }\n\n    @AssertWarning(\"NonAtomicOperationOnConcurrentMap\")\n    public void testAtomicUpdate2(String str) {\n        Integer res = chm.get(str);\n        chm.put(str, res + 1);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestAverageComputation.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestAverageComputation {\n    @AssertWarning(\"AverageComputationCouldOverflow\")\n    static int bsearch(int[] a, int low, int high, int key) {\n        while (low <= high) {\n            int mid = (low + high) >> 1;\n            int midVal = a[mid];\n\n            if (midVal < key)\n                low = mid + 1;\n            else if (midVal > key)\n                high = mid - 1;\n            else\n                return mid;\n        }\n        return -1;\n    }\n\n    @AssertWarning(\"AverageComputationCouldOverflow\")\n    static int bsearch2(int[] a, int low, int high, int key) {\n        while (low <= high) {\n            int mid = (low + high) / 2;\n            int midVal = a[mid];\n\n            if (midVal < key)\n                low = mid + 1;\n            else if (midVal > key)\n                high = mid - 1;\n            else\n                return mid;\n        }\n        return -1;\n    }\n\n    @AssertNoWarning(\"AverageComputationCouldOverflow\")\n    static int bsearchOk(int[] a, int low, int high, int key) {\n        while (low <= high) {\n            int mid = (low + high) >>> 1;\n            int midVal = a[mid];\n\n            if (midVal < key)\n                low = mid + 1;\n            else if (midVal > key)\n                high = mid - 1;\n            else\n                return mid;\n        }\n        return -1;\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestBadMath.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestBadMath {\n    private static final long FLAG_BAR = 0x100;\n    private static final long FLAG_FOO = 0x8000_0000_0000_0000L;\n    \n    @AssertWarning(\"RemOne\")\n    public int testRem(int x) {\n        int mod = 1;\n        Integer add = 0;\n        if (x == 2)\n            mod += add;\n        return x % mod;\n    }\n\n    @AssertWarning(\"RemOne\")\n    public int testRemSwitch(int x) {\n        int mod = 1;\n        switch(x) {\n        case 1:\n            System.out.println(\"Hehe\");\n            break;\n        case 2:\n            System.out.println(\"Haha\");\n            break;\n        default:\n            mod = 1;\n            break;\n        }\n        return x % mod;\n    }\n    \n    @AssertWarning(\"RemOne\")\n    public int testRemSwitchInside(int x) {\n        int mod = 1;\n        switch(x) {\n        case 1:\n            System.out.println(\"Hehe\");\n            mod = 1;\n        case 2:\n            System.out.println(\"Haha\");\n            return x % mod;\n        default:\n            mod = 2;\n            break;\n        }\n        return x % mod;\n    }\n    \n    @AssertNoWarning(\"RemOne\")\n    public int testRemSwitchOk(int x) {\n        int mod = 1;\n        switch(x) {\n        case 1:\n            System.out.println(\"Hehe\");\n            mod = 2;\n            break;\n        case 2:\n            System.out.println(\"Haha\");\n            break;\n        default:\n            mod = 1;\n            break;\n        }\n        return x % mod;\n    }\n    \n    @AssertWarning(\"RemOne\")\n    public int testRemLoop() {\n        int mod = 1;\n        for(int i=0; i<10; i++) {\n            if(i % mod > 0) {\n                System.out.println(\"Hehe\");\n            }\n        }\n        return 0;\n    }\n    \n    @AssertNoWarning(\"RemOne\")\n    public int testRemLoopOk() {\n        int mod = 1;\n        for (int i = 1; i < 10; i++) {\n            if (mod % i > 0) {\n                System.out.println(\"Hehe\");\n            }\n        }\n        return 0;\n    }\n    \n    @AssertWarning(\"RemOne\")\n    public int testRemDoWhile() {\n        int mod = 1;\n        int i = 0;\n        do {\n            if(i % mod > 0) {\n                System.out.println(\"Hehe\");\n            }\n        } while(++i < 10);\n        return 0;\n    }\n    \n    @AssertNoWarning(\"RemOne\")\n    public int testRemDoWhileOk() {\n        int mod = 1;\n        int i = 1;\n        do {\n            if(mod % i > 0) {\n                System.out.println(\"Hehe\");\n            }\n        } while(++i < 10);\n        return 0;\n    }\n    \n    @AssertWarning(\"RemOne\")\n    public int testRemAbs(int x) {\n        int mod = Math.abs(-1);\n        return x % mod;\n    }\n    \n    @AssertNoWarning(\"RemOne\")\n    public int testRemOk(int x) {\n        int mod = 1;\n        if (x == 2)\n            mod = 2;\n        return x % mod;\n    }\n\n    @AssertWarning(\"UselessOrWithZero\")\n    public int testOrZero(int x) {\n        int arg = 0;\n        return x | arg;\n    }\n    \n    @AssertWarning(\"UselessOrWithZero\")\n    public int testXorZero(int x) {\n        int arg = 0;\n        return x ^ arg;\n    }\n    \n    int flags;\n    byte byteFlags;\n    \n    static final int NOTHING = 0;\n    \n    @AssertNoWarning(\"UselessOrWithZero\")\n    public TestBadMath(int x) {\n        flags |= x;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void setNothingField() {\n        byteFlags |= NOTHING;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void setNothingLocal() {\n        byte f = byteFlags;\n        f |= NOTHING;\n        System.out.println(f);\n    }\n    \n    static long sFlags, sFlags2;\n    \n    @AssertNoWarning(\"UselessOrWithZero\")\n    public static void setFlags(int x) {\n        sFlags = 0;\n        if(x > 5)\n            sFlags = sFlags | x;\n    }\n    \n    @AssertWarning(\"UselessOrWithZero\")\n    public static void setFlags2(int x) {\n        sFlags = 0;\n        if(x > 5)\n            sFlags2 = sFlags | x;\n    }\n    \n    @AssertWarning(\"UselessAndWithMinusOne\")\n    public int testAndFFFF(int x) {\n        return x & 0xFFFFFFFF;\n    }\n    \n    @AssertNoWarning(\"UselessAndWithMinusOne\")\n    public long testAndFFFF(long x) {\n        return x & 0xFFFFFFFFL;\n    }\n    \n    @AssertWarning(\"UselessAndWithMinusOne\")\n    public long testAndFFFFIncorrect(long x) {\n        return x & 0xFFFFFFFF;\n    }\n    \n    @AssertWarning(\"UselessAndWithMinusOne\")\n    public long testAndFFFFConvert(long x) {\n        int mask = 0xFFFFFFFF;\n        return x & mask;\n    }\n    \n    @AssertNoWarning(\"Useless*\")\n    public boolean testBoolean() {\n        boolean x = false, z = false, y = true;\n        x |= y;\n        z = y | z;\n        return x ^ y ^ z;\n    }\n    \n    @AssertNoWarning(\"Useless*\")\n    public boolean testCompound() {\n        int x = 0;\n        int y = 0xFFFFFFFF;\n        x |= 0x100;\n        x |= 0x400;\n        y &= ~1;\n        return (x ^ y) > 0;\n    }\n    \n    @AssertWarning(\"CompareBitAndIncompatible\")\n    public void testIncompatibleAnd(int x) {\n        if((x & 2) == 1) {\n            System.out.println();\n        }\n    }\n    \n    @AssertWarning(\"CompareBitAndIncompatible\")\n    public void testIncompatibleAnd2(int x) {\n        if((x & 2) == 3) {\n            System.out.println();\n        }\n    }\n    \n    @AssertNoWarning(\"CompareBitAndIncompatible\")\n    public void testCompatibleAnd(int x) {\n        if((x & 3) == 2) {\n            System.out.println();\n        }\n    }\n\n    @AssertWarning(\"CompareBitOrIncompatible\")\n    public void testIncompatibleOr(int x) {\n        if((x | 1) == 2) {\n            System.out.println();\n        }\n    }\n    \n    @AssertWarning(\"CompareBitOrIncompatible\")\n    public void testIncompatibleOr2(int x) {\n        if((x | 3) == 2) {\n            System.out.println();\n        }\n    }\n    \n    @AssertNoWarning(\"CompareBitOrIncompatible\")\n    public void testCompatibleOr(int x) {\n        if((x | 1) == 3) {\n            System.out.println();\n        }\n    }\n    \n    @AssertWarning(\"CompareBitOrIncompatible\")\n    public void testInCompatibleOrObscure(int x) {\n        int mask = 0xFF;\n        int subMask = mask & 0x20;\n        if((x | mask) == subMask) {\n            System.out.println();\n        }\n    }\n\n    @AssertWarning(\"UselessAndWithMinusOne\")\n    public int testUselessAnd(long input) {\n        for(int i=0; i<input; i++) input--;\n        return (int)(input & 0xFFFFFFFF);\n    }\n    \n    @AssertWarning(\"BitCheckGreaterNegative\") \n    public boolean isFoo(long flags) {\n        return (flags & FLAG_FOO) > 0;\n    }\n\n    @AssertWarning(\"BitCheckGreaterNegative\") \n    public boolean isFooRev(long flags) {\n        return 0 < (flags & FLAG_FOO);\n    }\n    \n    @AssertNoWarning(\"*\") \n    public boolean isFooStrangeButOk(long flags) {\n        return (flags & FLAG_FOO) < 0;\n    }\n    \n    @AssertNoWarning(\"BitCheckGreaterNegative\") \n    @AssertWarning(\"BitCheckGreater\") \n    public boolean isBar(long flags) {\n        return (flags & FLAG_BAR) > 0;\n    }\n    \n    @AssertNoWarning(\"*\") \n    public boolean isFooOk(long flags) {\n        return (flags & FLAG_FOO) != 0;\n    }\n    \n    @AssertWarning(\"BitShiftInvalidAmount\")\n    public long shift(long input) {\n        return input >> 64;\n    }\n    \n    @AssertWarning(\"BitShiftInvalidAmount\")\n    public int shift(int input) {\n        return input >>> 32;\n    }\n    \n    @AssertWarning(\"BitShiftInvalidAmount\")\n    public int shiftLeft(int input) {\n        int amount = -1;\n        return input << amount;\n    }\n    \n    @AssertNoWarning(\"BitShiftInvalidAmount\")\n    public long shiftOk(long input) {\n        return input >> 32;\n    }\n    \n    @AssertNoWarning(\"BitShiftInvalidAmount\")\n    public int shiftOk(int input) {\n        return input >>> 31;\n    }\n    \n    @AssertWarning(\"BitAddSignedByte\")\n    public int bitAdd(int src, byte add) {\n        return (src << 8) + add;\n    }\n\n    @AssertWarning(\"BitAddSignedByte\")\n    public long bitAdd(long src, byte add) {\n        return (src << 8) + add;\n    }\n\n    @AssertWarning(\"BitAddSignedByte\")\n    public int bitAdd2(int src, byte add) {\n        return (src & 0xFFFFFF00) + add;\n    }\n\n    @AssertWarning(\"BitOrSignedByte\")\n    public int bitOr(int src, byte add) {\n        return (src << 8) | add;\n    }\n    \n    @Override\n    @AssertWarning(value=\"BitShiftWrongPriority\", minScore=70)\n    public int hashCode() {\n        return flags << 16 + byteFlags;\n    }\n\n    @AssertWarning(value=\"BitShiftWrongPriority\", minScore=45, maxScore=45)\n    public int testShiftPriority() {\n        return flags << 16 + byteFlags;\n    }\n\n    @AssertWarning(value=\"BitShiftWrongPriority\", minScore=55, maxScore=55)\n    public int testShiftPriorityAnd() {\n        return flags << 16 + (byteFlags & 0xFF);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestBadMethodCalls.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.File;\nimport java.math.BigDecimal;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\n\nimport org.junit.Assert;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestBadMethodCalls {\n    ScheduledThreadPoolExecutor ex = new ScheduledThreadPoolExecutor(10);\n    \n    Object arrField = Math.random()>0.5 ? new char[] {1,2,3} : new int[] {1,2,3};\n    \n    Queue<String> queue = new ConcurrentLinkedQueue<>(); \n    \n    @AssertWarning(value=\"SystemExit\", maxScore = 30)\n    public void systemExit() {\n        System.exit(0);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @AssertWarning(\"SystemRunFinalizersOnExit\")\n    public void runFinalizers() {\n        System.runFinalizersOnExit(true);\n    }\n    \n    @AssertWarning(value=\"SystemExit\", minScore = 40)\n    public void doSomething() {\n        System.exit(0);\n    }\n\n    @AssertWarning(value=\"SystemGc\", maxScore = 40)\n    public void collectSomeGarbage() {\n        System.gc();\n    }\n    \n    @AssertNoWarning(\"SystemGc\")\n    public void collectGarbageInCatch() {\n        try {\n            System.out.println();\n        }\n        catch(OutOfMemoryError ex) {\n            System.gc();\n        }\n        try {\n            System.out.println();\n        }\n        catch(StackOverflowError | OutOfMemoryError ex) {\n            System.gc();\n        }\n    }\n    \n    @AssertNoWarning(\"SystemGc\")\n    public void collectGarbageTimeMeasure() {\n        System.gc();\n        long start = System.nanoTime();\n        System.out.println();\n        long end = System.nanoTime();\n        System.out.println(end - start);\n    }\n    \n    @AssertWarning(\"SystemGc\")\n    public void collectGarbageInGeneralCatch() {\n        try {\n            System.out.println();\n        }\n        catch(Exception ex) {\n            System.gc();\n        }\n    }\n    \n    @AssertNoWarning(\"System*\")\n    public static void main(String[] args) {\n        System.gc();\n        System.exit(0);\n\t}\n\n    @SuppressWarnings(\"deprecation\")\n    @AssertWarning(\"ThreadStopThrowable\")\n    public void threadStopThrowable() {\n        Thread.currentThread().stop(new Exception());\n    }\n    \n    @SuppressWarnings(\"deprecation\")\n    @AssertNoWarning(\"ThreadStopThrowable\")\n    public void threadStop() {\n        Thread.currentThread().stop();\n    }\n\n    @AssertWarning(\"UselessThread\")\n    public String testCreateThread() {\n        return new Thread().getName();\n    }\n    \n    static class MyThread extends Thread\n    {\n        @AssertNoWarning(\"UselessThread\")\n        public MyThread() {\n        }\n        \n        @AssertWarning(\"UselessThread\")\n        public MyThread(int x) {\n            System.out.println(new Thread().getName()+x);\n        }\n        \n        @Override\n        public void run() {\n            System.out.println(\"My thread\");\n        }\n    }\n    \n    @AssertWarning(\"BigDecimalConstructedFromDouble\") \n    public BigDecimal testBigDecimal() {\n        return new BigDecimal(1.33);\n    }\n    \n    @AssertWarning(\"BigDecimalConstructedFromInfiniteOrNaN\") \n    public BigDecimal testBigDecimalInf() {\n        return new BigDecimal(Double.POSITIVE_INFINITY);\n    }\n    \n    @AssertNoWarning(\"BigDecimal*\") \n    public BigDecimal testBigDecimalRound(double x) {\n        return new BigDecimal(1.5).add(new BigDecimal(x));\n    }\n    \n    @AssertWarning(\"URLBlockingMethod\")\n    public int urlHashCode(URL url) {\n        return url.hashCode();\n    }\n    \n    @AssertWarning(\"URLBlockingMethod\")\n    public boolean urlEquals(URL url1, URL url2) {\n        return url1.equals(url2);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public boolean urlEquals(URL url, File file) throws MalformedURLException {\n        return url.equals(file.toURI().toURL());\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @AssertNoWarning(\"*\")\n    public boolean urlEquals2(URL url, File file) throws MalformedURLException {\n        return file.toURL().equals(url);\n    }\n    \n    @AssertWarning(\"ArrayToString\")\n    public String format(String prefix, int[] arr) {\n        return prefix+\":\"+arr;\n    }\n    \n    @AssertWarning(\"ArrayToString\")\n    public String format2(String suffix, int[] arr) {\n        return arr+\":\"+suffix;\n    }\n    \n    @AssertWarning(\"ArrayToString\")\n    public String fieldArray() {\n        return arrField.toString();\n    }\n    \n    @AssertWarning(\"ArrayToString\")\n    public String instanceOfToArray(Object obj) {\n        if(obj instanceof Number[]) {\n            return obj.toString();\n        }\n        return null;\n    }\n    \n    @AssertWarning(\"CharArrayToString\")\n    public String formatChar(char[] arr) {\n        return arr.toString();\n    }\n    \n    @AssertWarning(\"ArrayToString\")\n    public String format3(int[] arr) {\n        return arr.toString();\n    }\n    \n    @AssertNoWarning(\"ArrayToString\")\n    public String format4(String arr) {\n        return arr+arr;\n    }\n    \n    @AssertWarning(\"ArrayHashCode\")\n    public int hash(String str, int[] arr) {\n        return str.hashCode()*31+arr.hashCode();\n    }\n    \n    @AssertWarning(\"ArrayHashCode\")\n    public int hash2(String str, int[] arr) {\n        return Objects.hashCode(str)*31+Objects.hashCode(arr);\n    }\n    \n    @AssertWarning(\"ArrayHashCode\")\n    public int hash3(String str, int[] arr) {\n        return Objects.hash(str, arr);\n    }\n    \n    @AssertWarning(\"DoubleLongBitsToDoubleOnInt\")\n    public double testDouble(int x) {\n        return Double.longBitsToDouble(x);\n    }\n\n    @AssertWarning(\"ScheduledThreadPoolExecutorChangePoolSize\")\n    public void testThreadPoolExecutor(int poolSize) {\n        ex.setMaximumPoolSize(poolSize);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @AssertWarning(\"DateBadMonth\")\n    public void testBadMonth(Date date) {\n        date.setMonth(12);\n    }\n    \n    @AssertWarning(\"CollectionAddedToItself\") \n    public void testAddCollection(Collection<Object> c) {\n        c.add(c);\n    }\n\n    @AssertWarning(\"CollectionAddedToItself\") \n    public void testAddArrayList(ArrayList<Object> c) {\n        c.add(c);\n    }\n    \n    @AssertWarning(\"NullCheckMethodForConstant\")\n    public void testNullCheckAssert() {\n        Objects.requireNonNull(\"test\");\n    }\n\n    @AssertNoWarning(\"NullCheckMethodForConstant\")\n    public void testNullCheckAssertOk() {\n        Objects.requireNonNull(null);\n    }\n\n    @AssertWarning(\"NullCheckMethodForConstant\")\n    public void testNullCheckAssert2() {\n        String s = \"test\";\n        Assert.assertNotNull(s);\n    }\n    \n    @AssertWarning(\"WrongArgumentOrder\")\n    public void testWrongAssert(String str) {\n        Assert.assertNotNull(str, \"String is null\");\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testCorrectAssert(String str) {\n        Assert.assertNotNull(\"String is null\", str);\n    }\n    \n    @AssertWarning(\"WrongArgumentOrder\")\n    public void testWrongPrecondition(String str) {\n        Objects.requireNonNull(\"String is null\", str);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testCorrectPrecondition(String str) {\n        Objects.requireNonNull(str, \"String is null\");\n    }\n\n    private final int[] state = new int[10];\n    \n    public TestBadMethodCalls() {\n    }\n\n    // Two constructors are necessary to test merging of state initialization\n    public TestBadMethodCalls(int x) {\n        System.out.println(x);\n    }\n    \n    @Override\n    @AssertWarning(\"ArrayHashCode\")\n    public int hashCode() {\n        return state.hashCode();\n    }\n\n    @AssertWarning(\"StreamToString\")\n    public String process(List<String> vals) {\n        return vals.stream().map(String::trim).toString();\n    }\n    \n    @AssertWarning(\"ConcurrentCollectionSize\")\n    public int size() {\n        return queue.size();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestBadMethodReferences.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Random;\n\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestBadMethodReferences {\n    @AssertWarning(\"MaxMinMethodReferenceForComparator\")\n    public void test() {\n        new Random().ints(1000).boxed().sorted(Integer::max).forEach(System.out::println);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestBadMonitorObject.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestBadMonitorObject {\n    @AssertWarning(\"SynchronizationOnBoxedNumber\")\n    public void syncNumber() {\n        Integer num = 1;\n        synchronized(num) {\n            System.out.println(\"In lock\");\n        }\n    }\n\n    @AssertWarning(\"SynchronizationOnUnsharedBoxed\")\n    public void syncUnsharedNumber() {\n        synchronized(new Integer(1)) {\n            System.out.println(\"In lock\");\n        }\n    }\n\n    @AssertWarning(\"SynchronizationOnUnsharedBoxed\")\n    public void syncUnsharedBoolean() {\n        synchronized(new Boolean(true)) {\n            System.out.println(\"In lock\");\n        }\n    }\n    \n    @AssertWarning(\"SynchronizationOnBoolean\")\n    public void syncBoolean() {\n        synchronized(Boolean.TRUE) {\n            System.out.println(\"In lock\");\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestCheckReturnValue.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.Reader;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestCheckReturnValue {\n    @AssertWarning(\"ReturnValueOfRead\")\n    public void read(InputStream is, byte[] arr) throws IOException {\n        is.read(arr);\n    }\n\n    @AssertWarning(\"ReturnValueOfSkip\")\n    public void skipTen(Reader r) throws IOException {\n        r.skip(10);\n    }\n\n    @AssertNoWarning(\"ReturnValueOfSkip\")\n    public void skipTenOk(Reader r) throws IOException {\n        long skip = 10;\n        while(skip > 0) {\n            skip -= r.skip(skip);\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void readOk(InputStream is, byte[] arr) throws IOException {\n        if(is.read(arr) != arr.length) {\n            throw new IOException(\"Not fuly read\");\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestCloneContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestCloneContract {\n    @AssertWarning(\"CloneableDoesNotImplementClone\")\n    public class Clone0 implements Cloneable {\n        // empty\n    }\n    \n    public class Clone1 implements Cloneable {\n        public int f = 0;\n        \n        @Override\n        @AssertWarning(value=\"CloneableNoSuperCall\", minScore=45)\n        protected Object clone() throws CloneNotSupportedException {\n            return new Clone1();\n        }\n    }\n\n    public class Clone2 extends Clone1 {\n        @Override\n        @AssertWarning(value=\"CloneableNoSuperCall\", minScore=35, maxScore=44)\n        protected Object clone() throws CloneNotSupportedException {\n            return new Clone2();\n        }\n    }\n    \n    @Override\n    @AssertWarning(\"NotCloneableHasClone\")\n    public TestCloneContract clone() {\n        return new TestCloneContract();\n    }\n    \n    public class NotCloneable {\n        @Override\n        @AssertNoWarning(\"*\")\n        public Object clone() {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestCompareContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Comparator;\n\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author shustkost\n *\n */\npublic class TestCompareContract implements Comparable<TestCompareContract>{\n\n    @Override\n    @AssertWarning(\"CompareReturnsMinValue\")\n    public int compareTo(TestCompareContract o) {\n        return o == this ? 0 : Integer.MIN_VALUE;\n    }\n    \n    Comparator<String> CMP = new Comparator<String>() {\n        @Override\n        @AssertWarning(\"CompareReturnsMinValue\")\n        public int compare(String o1, String o2) {\n            if(o1.isEmpty())\n                return Integer.MIN_VALUE;\n            if(o2.isEmpty())\n                return Integer.MAX_VALUE;\n            return o1.compareTo(o2);\n        }\n    };\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestCompareUsage.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Comparator;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestCompareUsage {\n    @AssertWarning(\"NegatingComparatorResult\")\n    public boolean less(String s1, String s2) {\n        int res = s1.compareTo(s2);\n        if(s1.isEmpty())\n            res = -1;\n        int neg = -res;\n        return neg > 0;\n    }\n\n    @AssertWarning(\"ComparingComparatorResultWithNumber\")\n    public boolean greater(String s1, String s2) {\n        return 1 == s1.compareTo(s2);\n    }\n\n    @AssertWarning(\"ComparingComparatorResultWithNumber\")\n    public boolean greater2(String s1, String s2) {\n        int res = s1.compareTo(s2);\n        return res == 1;\n    }\n    \n    @AssertNoWarning(\"ComparingComparatorResultWithNumber\")\n    public boolean eq(String s1, String s2) {\n        return s1.compareTo(s2) == 0;\n    }\n    \n    @AssertNoWarning(\"NegatingComparatorResult\")\n    public boolean ok(String s1, String s2) {\n        int res = s1.isEmpty() ? 1 : -1;\n        int neg = -res;\n        return neg > 0;\n    }\n\n    @AssertWarning(\"NegatingComparatorResult\")\n    public boolean lessCmp(String s1, String s2) {\n        return -Comparator.<String>naturalOrder().compare(s1, s2) > 0;\n    }\n\n    @AssertNoWarning(\"NegatingComparatorResult\")\n    public boolean lessOk(String s1, String s2) {\n        return s2.compareTo(s1) > 0;\n    }\n    \n    @AssertNoWarning(\"NegatingComparatorResult\")\n    public boolean lessCmpOk(String s1, String s2) {\n        return Comparator.<String>naturalOrder().compare(s2, s1) > 0;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestConditionChain.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestConditionChain {\n    @AssertWarning(\"SameConditions\")\n    public void testSameConditions(int x) {\n        if (x > 2) {\n            if (2 < x) {\n                System.out.println(x);\n            }\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testExcludingConditions(float x) {\n        if (x <= 0) {\n            System.out.println(\"ok\");\n        } else if (x > 0) {\n            System.out.println(x);\n        }\n    }\n\n    @AssertWarning(\"SameConditions\")\n    public void testSameConditionsElse(int x) {\n        if (x > 2) {\n            if (x > 2) {\n                System.out.println(x);\n            } else {\n                System.out.println(\"foo\");\n            }\n        }\n    }\n\n    @AssertWarning(\"SameConditions\")\n    public void testSameConditionsChain(int x) {\n        if (x > 2) {\n            if (x < 4) {\n                if (x > 2) {\n                    System.out.println(x);\n                }\n            }\n        }\n    }\n\n    @AssertWarning(\"SameConditions\")\n    public void testSameConditionsChainElse1(int x) {\n        if (x > 2) {\n            if (x < 4) {\n                if (x > 2) {\n                    System.out.println(x);\n                } else {\n                    System.out.println(\"false\");\n                }\n            }\n        }\n    }\n\n    @AssertWarning(\"SameConditions\")\n    public void testSameConditionsChainElse2(int x) {\n        if (x > 2) {\n            if (x < 4) {\n                if (x > 2) {\n                    System.out.println(x);\n                } else {\n                    System.out.println(\"false\");\n                }\n            } else {\n                System.out.println(\"false\");\n            }\n        }\n    }\n\n    @AssertWarning(\"SameConditionsExcluding\")\n    public void testSameConditionsExcluding(int x) {\n        if (x > 2) {\n            System.out.println(\"foo3\");\n        } else if (x < 4) {\n            if (x > 2) {\n                System.out.println(x);\n            } else {\n                System.out.println(\"foo1\");\n            }\n        } else {\n            System.out.println(\"foo2\");\n        }\n    }\n\n    @AssertNoWarning(\"SameConditionsExcluding\")\n    @AssertWarning(value=\"SameConditions\", maxScore = 50)\n    void testCondition(int vc, short value) {\n        for (int i = vc; --i >= 0;) {\n            if (value <= 0)\n                System.out.println(\"1\");\n            else if (value > 0)\n                System.out.println(\"2\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestCovariantArrays.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Arrays;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestCovariantArrays {\n    final Object[] array = new String[] {\"1\", \"2\"};\n    static abstract class Parent {}\n    static class Child extends Parent {}\n    static class GrandChild extends Child {}\n\n    @AssertWarning(\"ContravariantArrayStore\")\n    public void test() {\n        array[1] = 2;\n    }\n\n    @AssertNoWarning(\"*\")\n    public void test2d() {\n        Number[][] arr = new Number[1][];\n        arr[0] = new Integer[10];\n        System.out.println(Arrays.deepToString(arr));\n    }\n    \n    @AssertWarning(\"ContravariantArrayStore\")\n    public void caaStore(boolean b) {\n        Object[] numbers = new Integer[10];\n        if(b)\n            numbers = new Long[10];\n        numbers[0] = \"abc\";\n    }\n\n    @AssertWarning(\"ContravariantArrayStore\")\n    public <T extends Number> void genericStore(T val) {\n        Object[] numbers = new String[10];\n        numbers[0] = val;\n    }\n\n    @AssertNoWarning(\"ContravariantArrayStore\")\n    public <T extends Number> void testNull() {\n        String[] numbers = new String[10];\n        numbers[0] = null;\n    }\n    \n    // parent is Child[] array, but every non-abstract subclass of Parent is also subclass of Child\n    // so no ArrayStoreException will occur in current project and we don't report this method\n    @AssertNoWarning(\"ContravariantArrayStore\")\n    public void caaStoreNoReport(Parent p) {\n        Parent[] parents = new Child[10];\n        parents[0] = p;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testTernaryPhi() {\n        Object[] e = new String[10];\n        Object a = \"1\";\n        for(int i=0; i<e.length; i++) {\n            a = i % 2 == 0 ? a : 123;\n            e[i] = a;\n        }\n        System.out.println(Arrays.toString(e));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestDeadLocalStore.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestDeadLocalStore {\n    int x;\n\n    @AssertWarning(value = \"ParameterOverwritten\", minScore = 55)\n    public void testDeadLocalSimple(int x) {\n        x = 10;\n        System.out.println(x);\n    }\n\n    @AssertNoWarning(\"ParameterOverwritten\")\n    public void testDeadLocalBranch(int x) {\n        if (Math.random() > 0.5)\n            x = 10;\n        System.out.println(x);\n    }\n\n    class Extension extends TestDeadLocalStore {\n        @Override\n        @AssertWarning(value = \"ParameterOverwritten\", maxScore = 40)\n        public void testDeadLocalSimple(int x) {\n            x = 10;\n            System.out.println(x);\n        }\n    }\n\n    @AssertWarning(\"DeadIncrementInReturn\")\n    public int testDeadIncrement(int x) {\n        return x++;\n    }\n\n    @AssertNoWarning(\"*\")\n    public int testDeadIncrementOk(int x) {\n        x++;\n        return x;\n    }\n\n    @AssertNoWarning(\"DeadIncrementInReturn\")\n    public int testFieldIncrement() {\n        return x++;\n    }\n\n    @SuppressWarnings(\"unused\")\n    @AssertWarning(\"DeadStoreInReturn\")\n    public boolean testDeadStore(boolean b) {\n        return b = true;\n    }\n\n    @AssertWarning(\"DeadIncrementInAssignment\")\n    public void testDeadIncrementAssignment(int i) {\n        i = i++;\n        System.out.println(i);\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDeadIncrementAssignment2(int i) {\n        int x = i++;\n        System.out.println(i);\n        i = x;\n        System.out.println(i);\n    }\n\n    @AssertWarning(\"DeadParameterStore\")\n    public void testDeadParameterStore(int i) {\n        if (i > 0) {\n            i = 0;\n        }\n    }\n\n    @AssertWarning(value = \"DeadLocalStore\", maxScore = 45)\n    public void testDeadLocalStore(int i) {\n        int x = 0;\n        if (i > x) {\n            x = 1;\n        }\n        System.out.println(i);\n    }\n\n    @AssertWarning(value = \"DeadLocalStore\", minScore = 46)\n    public void testDeadLocalStore2(int i) {\n        int x = 1;\n        if (i > x) {\n            x = 2;\n        }\n        System.out.println(i);\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDeadLocalStoreSame(int i) {\n        int x = 1;\n        if (i > x) {\n            x = 1;\n        }\n        System.out.println(x);\n    }\n\n    @AssertNoWarning(\"Dead*\")\n    public void testSystemExit(String s) {\n        int x = 0;\n        try {\n            x = Integer.parseInt(s);\n        }\n        catch(NumberFormatException ex) {\n            System.out.println(\"Ooops\");\n            System.exit(1);\n        }\n        System.out.println(x);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testDeadLocalStoreLambda() {\n        double x = Math.random();\n        System.out.println(new Random().doubles(1000).filter(d -> d > x).count());\n    }\n\n    @AssertNoWarning(\"*\")\n    public int testDeadLocalStoreLambda2(boolean b) {\n        if (b) {\n            double x = Math.random();\n            System.out.println(new Random().doubles(1000).filter(d -> d > x).count());\n            return 1;\n        }\n        return 0;\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDeadLocalStoreConst(int i) {\n        final int x = 123456;\n        if (i > x) {\n            System.out.println(i);\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDeadLocalStoreConstStr(int i) {\n        final String x = \"test\";\n        if (i > x.length()) {\n            System.out.println(i);\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDeadLocalStoreCatch(String i) {\n        try {\n            System.out.println(Integer.parseInt(i));\n        } catch (NumberFormatException | NullPointerException ex) {\n            System.out.println(\"none\");\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void execute(Runnable r) throws InstantiationException, NoSuchMethodException, SecurityException {\n        try {\n            r.getClass().getConstructor().newInstance().run();\n        } catch (IllegalArgumentException | IllegalAccessException e) {\n            throw new AssertionError(e);\n        } catch (InvocationTargetException e) {\n            Throwable cause = e.getCause();\n            if (cause instanceof RuntimeException) {\n                throw (RuntimeException) cause;\n            } else if (cause instanceof Error) {\n                throw (Error) cause;\n            }\n            throw new RuntimeException(cause);\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDeadLocalStoreTernaryOk(int b) {\n        int x = 2;\n        if (b > 0) {\n            x = b % 2 == 0 ? 3 : 4;\n        }\n        System.out.println(x);\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDeadLocalStoreTernaryOk2(int b) {\n        int c;\n        if (b > 0)\n            c = b % 2 == 0 ? 1 : 2;\n        else\n            c = b % 2 == 0 ? 3 : 4;\n        System.out.println(c);\n    }\n\n    //    @AssertWarning(\"UnusedLocalVariable\")\n    //    public void testDeadLocalStoreTernary(boolean flag, int a, int b) {\n    //        int c = flag ? a : b;\n    //        System.out.println(a);\n    //        System.out.println(b);\n    //    }\n\n    @AssertNoWarning(\"*\")\n    public void testThrow(String i) {\n        if (i.length() > 2) {\n            final String s = \"exception message\";\n            throw new RuntimeException(i + s);\n        }\n    }\n\n    @AssertNoWarning(\"DeadParameterStore\")\n    public void testDeadLocalStoreCatch2(String i) {\n        try {\n            System.out.println(Integer.parseInt(i));\n        } catch (NumberFormatException ex) {\n            i = \"abc\";\n        } catch (Exception ex) {\n            return;\n        }\n        System.out.println(i);\n    }\n\n    static class MyException extends Exception {\n        private static final long serialVersionUID = 1L;\n    };\n\n    private int convert(String s) throws MyException {\n        if (s.isEmpty())\n            throw new MyException();\n        return Integer.parseInt(s);\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDeadLocalStoreCatch3(String i) {\n        boolean flag = false;\n        try {\n            if (i != null) {\n                System.out.println(convert(i));\n            } else {\n                flag = true;\n            }\n        } catch (MyException ex) {\n            flag = true;\n        }\n        if (flag) {\n            throw new RuntimeException();\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDeadLocalStoreLabel(boolean x, String a) {\n        if (x ? !a.equals(\"x\") : !a.equals(\"y\"))\n            return;\n        int i = 1;\n        System.out.println(i);\n    }\n\n    @AssertWarning(value = \"DeadLocalStore\", maxScore = 35)\n    public void testDeadLocalStoreInit(boolean x) {\n        int a = 0;\n        if (x) {\n            a = 1;\n        } else {\n            a = 2;\n        }\n        System.out.println(a);\n    }\n\n    public void testLocalClass() {\n        class X {\n            @AssertWarning(\"ParameterOverwritten\")\n            public void print(int x) {\n                x = 10;\n                System.out.println(x);\n            }\n        }\n        new X().print(5);\n    }\n\n    static class TestClass {\n        long someCalculation(long i) {\n            return i;\n        }\n    }\n\n    // TODO: procyon bug\n    //@AssertNoWarning(\"UnusedLocalVariable\")\n    public void testUnusedLocalVariableClassInStreamMap() {\n        // class vs primitive has some difference, i could guess\n        // or maybe .map()'s complexity is the key to warning failure \n        TestClass tc = new TestClass();\n        final List<Long> numbers = Stream.of(1L, 2L, 3L).map(i -> {\n            if (i == 2L) {\n                return 11L;\n            }\n            return tc.someCalculation(i);\n        }).collect(Collectors.toList());\n        System.out.println(numbers.size());\n    }\n    \n    @AssertWarning(value = \"DeadLocalStore\", maxScore = 35)\n    public void testAndChain(int x) {\n        int a = 0;\n        if(x > 2 && ((a = x*2) > 5)) {\n            System.out.println(a);\n        }\n    }\n\n    @AssertWarning(\"DeadLocalStore\")\n    public void testLabel(int i, int j) {\n        int x = 2;\n        outer:\n        while(i < 10) {\n            x = 3;\n            while(j < 20) {\n                x = 4;\n                if (i + j + x == 20) {\n                    x = 5;\n                    break outer;\n                }\n            }\n            x = 5;\n        }\n        if(x != 5) {\n            System.out.println(\"Never\");\n        }\n    }\n    \n    @AssertWarning(\"DeadLocalStore\")\n    public int[] testUnreachableCatch() {\n        int[] a = null;\n        try {\n            a = new int[20];\n        } catch(Exception ex) {\n            System.out.println(\"Exceptional\");\n        }\n        return a;\n    }\n    \n    @AssertWarning(\"DeadLocalStore\")\n    public void synchronizedTest() {\n        int a = 10;\n        synchronized(this) {\n            a = 20;\n            testLocalClass();\n        }\n        System.out.println(a);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestDroppedExceptionObject.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestDroppedExceptionObject {\n    @AssertWarning(\"DroppedException\")\n    public void test(String s) {\n        if(s.isEmpty())\n            new IllegalArgumentException(s);\n        System.out.println(s);\n    }\n\n    @AssertNoWarning(\"DroppedException\")\n    public void testOk(String s) {\n        if(s.isEmpty())\n            throw new IllegalArgumentException(s);\n        System.out.println(s);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestDubiousCatch.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.ConcurrentModificationException;\nimport java.util.List;\n\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestDubiousCatch {\n    Object obj = new Object();\n\n    @AssertWarning(\"CatchIllegalMonitorStateException\")\n    public boolean catchIllegalState() {\n        try {\n            obj.wait();\n            return true;\n        } catch (InterruptedException | IllegalMonitorStateException e) {\n            return false;\n        }\n    }\n\n    @AssertWarning(\"CatchConcurrentModificationException\")\n    public int catchCME(List<String> list) {\n        try {\n            int i = 0;\n            for (String s : list) {\n                if (s.equals(\"test\"))\n                    i++;\n            }\n            return i;\n        } catch (ConcurrentModificationException e) {\n            return 0;\n        }\n    }\n\n    @AssertWarning(\"CatchConcurrentModificationException\")\n    public void catchCMEVoid(List<String> list) {\n        try {\n            for (String s : list) {\n                if (s.equals(\"test\"))\n                    System.out.println(s);\n            }\n        } catch (ConcurrentModificationException e) {\n            // ignore\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestDuplicateAssignment.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestDuplicateAssignment {\n    int x = 1, y;\n    \n    @AssertNoWarning(\"FieldDoubleAssignment\")\n    public TestDuplicateAssignment(int a) {\n        x = a;\n    }\n    \n    @SuppressWarnings(\"cast\")\n    @AssertWarning(\"FieldDoubleAssignment\")\n    public void doubleFieldChain(int val) {\n        this.x = (int)(this.x = Math.abs(val));\n    }\n    \n    @AssertWarning(\"FieldDoubleAssignment\")\n    public void doubleField(int val) {\n        this.x = Math.abs(val);\n        this.x = Math.addExact(val, val);\n    }\n    \n    @AssertNoWarning(\"FieldDoubleAssignment\")\n    public void doubleFieldReuse(int val) {\n        this.x = Math.abs(val);\n        this.x = this.x * 2 + 1;\n    }\n    \n    @AssertNoWarning(\"FieldDoubleAssignment\")\n    public void doubleDiffField(int val) {\n        this.x = Math.abs(val);\n        this.y = Math.addExact(val, val);\n    }\n    \n    @AssertWarning(\"FieldDoubleAssignment\")\n    public void doubleDiffFieldSameField(int val) {\n        this.x = Math.abs(val);\n        this.y = Math.addExact(val, val);\n        this.x = Math.decrementExact(val);\n    }\n    \n    @AssertWarning(\"FieldDoubleAssignment\")\n    public void trickyReceiver(TestDuplicateAssignment tda, int val) {\n        TestDuplicateAssignment tda2 = tda;\n        tda2.x = tda.x = val;\n    }\n    \n    @AssertWarning(\"FieldDoubleAssignment\")\n    public void array(TestDuplicateAssignment[] arr, int val) {\n        arr[val].x = val;\n        arr[val].x = val;\n    }\n\n    @AssertNoWarning(\"FieldDoubleAssignment\")\n    public void arrayOk(TestDuplicateAssignment[] arr, int val) {\n        arr[0].x = val;\n        arr[1].x = val;\n    }\n    \n    @AssertNoWarning(\"FieldDoubleAssignment\")\n    public void doubleDiffObject(TestDuplicateAssignment tda, int val) {\n        this.x = Math.abs(val);\n        tda.x = Math.addExact(val, val);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestEasyMockProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport org.easymock.EasyMock;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestEasyMockProblems {\n    @AssertWarning(\"UselessEasyMockCall\")\n    public void testEasyMock() {\n        EasyMock.replay();\n    }\n\n    @AssertWarning(\"UselessEasyMockCall\")\n    public void testEasyMock2() {\n        Object[] data = {};\n        EasyMock.resetToDefault(data);\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testEasyMockOk() {\n        Object[] data = {\"\"};\n        EasyMock.verify(data);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestEmptySync.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestEmptySync {\n    @AssertWarning(\"EmptySynchronizeBlock\")\n    public void emptySync() {\n        System.out.println(\"Before\");\n        synchronized (this) {\n            // empty\n        }\n        System.out.println(\"After\");\n    }\n\n    @AssertNoWarning(\"EmptySynchronizeBlock\")\n    public void nonEmptySync() {\n        System.out.println(\"Before\");\n        synchronized (this) {\n            System.out.println(\"Inside\");\n        }\n        System.out.println(\"After\");\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestEqualsContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.awt.Point;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Objects;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestEqualsContract {\n\n    @Override\n    @AssertWarning(value=\"EqualsReturnsFalse\", minScore = 40)\n    public boolean equals(Object obj) {\n        return false;\n    }\n\n    static class NonPublic {\n        @Override\n        @AssertWarning(value=\"EqualsReturnsTrue\", maxScore = 30)\n        public boolean equals(Object obj) {\n            return true;\n        }\n\n        @Override\n        public int hashCode() {\n            return 42;\n        }\n    }\n\n    public static class SubClass2 extends NonPublic {\n        int f;\n\n        @Override\n        @AssertWarning(\"EqualsNoHashCode\")\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (!super.equals(obj) || getClass() != obj.getClass())\n                return false;\n            SubClass2 other = (SubClass2) obj;\n            return f == other.f;\n        }\n    }\n\n    public static final class Final {\n        @Override\n        @AssertWarning(value=\"EqualsReturnsFalse\", minScore = 35, maxScore = 45)\n        @AssertNoWarning(\"EqualsObjectHashCode\")\n        public boolean equals(Object obj) {\n            return false;\n        }\n    }\n\n    public static final class ClassName {\n        public int i;\n\n        @Override\n        @AssertWarning(\"EqualsClassNames\")\n        public boolean equals(Object obj) {\n            if (!\"one.util.huntbugs.testdata.TestEqualsContract.ClassName\".equals(obj.getClass().getName()))\n                return false;\n            ClassName other = (ClassName) obj;\n            return i == other.i;\n        }\n    }\n\n    @AssertWarning(\"EqualsOther\")\n    public static class OtherEquals {\n        public boolean equals(TestEqualsContract other) {\n            return other != null;\n        }\n    }\n\n    @AssertWarning(\"EqualsSelf\")\n    public static class SelfEquals {\n        public boolean equals(SelfEquals other) {\n            return other == this;\n        }\n\n        @Override\n        public int hashCode() {\n            return 42;\n        }\n    }\n\n    public static class SubClass extends SelfEquals {\n        int f;\n\n        @Override\n        @AssertWarning(\"EqualsNoHashCode\")\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (!super.equals(obj) || getClass() != obj.getClass())\n                return false;\n            SubClass other = (SubClass) obj;\n            return f == other.f;\n        }\n    }\n\n    public static class SubClassNoFields extends SelfEquals {\n        @Override\n        @AssertNoWarning(\"EqualsNoHashCode\")\n        public boolean equals(Object obj) {\n            return obj instanceof SubClassNoFields && super.equals(obj);\n        }\n    }\n    \n    public static class SubClassNoFields1 extends SelfEquals {\n        @Override\n        @AssertNoWarning(\"EqualsNoHashCode\")\n        public boolean equals(Object obj) {\n            return obj == this || obj instanceof SubClassNoFields && super.equals(obj);\n        }\n    }\n    \n    public static class SubClassNoFields1a extends SelfEquals {\n        @Override\n        @AssertNoWarning(\"EqualsNoHashCode\")\n        public boolean equals(Object obj) {\n            return this == obj || obj instanceof SubClassNoFields && super.equals(obj);\n        }\n    }\n    \n    public static class SubClassNoFields2 extends SelfEquals {\n        @Override\n        @AssertNoWarning(\"EqualsNoHashCode\")\n        public boolean equals(Object obj) {\n            if(!(obj instanceof SubClassNoFields2))\n                return false;\n            return super.equals(obj);\n        }\n    }\n    \n    @AssertWarning(\"EqualsEnum\")\n    @AssertNoWarning(\"EqualsSelf\")\n    public static enum EnumEquals {\n        A, B, C;\n\n        public boolean equals(EnumEquals other) {\n            return other == this;\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public static class EqualsOk {\n        public boolean equals(EqualsOk other, int check) {\n            return other != this && check > 2;\n        }\n    }\n\n    public static class HashCodeObject {\n        @Override\n        @AssertWarning(\"HashCodeObjectEquals\")\n        public int hashCode() {\n            return 42;\n        }\n    }\n\n    public static class HashCodeList extends ArrayList<String> {\n        private static final long serialVersionUID = 1L;\n\n        String myField;\n\n        public HashCodeList(String myField) {\n            this.myField = myField;\n        }\n\n        @Override\n        @AssertWarning(\"HashCodeNoEquals\")\n        public int hashCode() {\n            return super.hashCode() * 31 + myField.hashCode();\n        }\n    }\n\n    public static class EqualsObject {\n        public int f;\n\n        @Override\n        @AssertWarning(value=\"EqualsObjectHashCode\", maxScore = 48, minScore = 43)\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (obj == null || getClass() != obj.getClass())\n                return false;\n            EqualsObject other = (EqualsObject) obj;\n            return f == other.f;\n        }\n    }\n\n    static class EqualsObject2 {\n        public int f;\n\n        @Override\n        @AssertWarning(value=\"EqualsObjectHashCode\", maxScore = 32, minScore = 22)\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (obj == null || getClass() != obj.getClass())\n                return false;\n            EqualsObject other = (EqualsObject) obj;\n            return f == other.f;\n        }\n    }\n\n    @AssertNoWarning(\"Equals*\")\n    public static class EqualsList extends ArrayList<String> {\n        private static final long serialVersionUID = 1L;\n        public int f;\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (!super.equals(obj) || getClass() != obj.getClass())\n                return false;\n            EqualsList other = (EqualsList) obj;\n            return f == other.f;\n        }\n    }\n    \n    public static class EqualsWrongField {\n        private int x;\n        private int y;\n        private int[] arr1;\n        private int[] arr2;\n\n        @Override\n        public int hashCode() {\n            final int prime = 31;\n            int result = 1;\n            result = prime * result + x;\n            result = prime * result + y;\n            return result;\n        }\n        \n        @Override\n        @AssertWarning(\"EqualsSuspiciousFieldComparison\")\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (obj == null || getClass() != obj.getClass())\n                return false;\n            EqualsWrongField other = (EqualsWrongField) obj;\n            return x == other.y && Arrays.equals(arr1, other.arr2);\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public static class EqualsFieldsOk {\n        private final int x;\n        private final int y;\n        \n        public EqualsFieldsOk(int x, int y) {\n            this.x = x;\n            this.y = y;\n        }\n        \n        public Point getPoint() {\n            return new Point(x, y);\n        }\n        \n        @Override\n        public int hashCode() {\n            return Objects.hash(x, y);\n        }\n        \n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj)\n                return true;\n            if (obj == null || getClass() != obj.getClass())\n                return false;\n            Point pt = ((EqualsFieldsOk) obj).getPoint();\n            return x == pt.x && y == pt.y;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestExceptionalExpression.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestExceptionalExpression {\n    @AssertWarning(\"ExceptionalExpression\")\n    public void testDivisionByZero() {\n        int a = 1;\n        int b = 0;\n        System.out.println(a/b);\n    }\n\n    @AssertWarning(value=\"ExceptionalExpression\", maxScore=60)\n    public void testNFE() {\n        String s = \"test\";\n        try {\n            System.out.println(Integer.parseInt(s));\n        }\n        catch(NumberFormatException nfe) {\n            System.out.println(\"Well...\");\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestExclusiveConditions.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.concurrent.TimeUnit;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestExclusiveConditions {\n    @AssertWarning(\"AndEqualsAlwaysFalse\")\n    public void testSimple(int x) {\n        if(x == 1 && x == 2) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testDiff(int x, int y) {\n        if(x == 1 && y == 2) {\n            System.out.println(\"Possible\");\n        }\n    }\n    \n    @AssertWarning(\"OrNotEqualsAlwaysTrue\")\n    public void testOr(int x) {\n        if(x != 1 || x != 2) {\n            System.out.println(\"Always!\");\n        }\n    }\n\n    @AssertWarning(\"AndEqualsAlwaysFalse\")\n    public void testBoxing(Integer x) {\n        Integer a = 1;\n        Integer b = 2;\n        if(x == a && x == b) {\n            System.out.println(\"Never!\");\n        }\n    }\n    \n    @AssertWarning(\"AndEqualsAlwaysFalse\")\n    public void testStrings(String str) {\n        if(str.equals(\"A\\nB\") && str.equals(\"B\\nA\")) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"AndEqualsAlwaysFalse\")\n    public void testEnum(TimeUnit tu) {\n        if(tu.equals(TimeUnit.DAYS) && tu == TimeUnit.HOURS) {\n            System.out.println(\"Never!\");\n        }\n    }\n    \n    @AssertWarning(\"AndEqualsAlwaysFalse\")\n    public void testComplex(int x, int y, int z) {\n        if(x == 1 && y == 2 && z == 3 && 4 == x) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"OrNotEqualsAlwaysTrue\")\n    public void testComplexOr(int x, int y, int z) {\n        if(x != 1 || y != 2 || z != 3 || 4 != x) {\n            System.out.println(\"Always!\");\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestExposeRepresentation.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Hashtable;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestExposeRepresentation {\n    private class InternalClass {\n        private int[] f;\n        \n        @AssertNoWarning(\"*\")\n        public void setField(int[] f) {\n            this.f = f;\n        }\n    }\n    \n    int[] f;\n    static Hashtable<String, Integer> ht;\n    InternalClass ic = new InternalClass();\n    Point p;\n\n    @AssertWarning(value=\"ExposeMutableFieldViaParameter\", minScore=30)\n    public void setField(int[] f) {\n        if(f.length > 2)\n            this.f = f;\n        if(ic.f == null)\n            ic.setField(f);\n    }\n    \n    @AssertNoWarning(\"ExposeMutableFieldViaParameter\")\n    public void setFieldClone(int[] f) {\n        f = f.clone();\n        this.f = f;\n    }\n    \n    @AssertWarning(value=\"ExposeMutableFieldViaParameter\", maxScore=29)\n    public void setFieldVarArgs(int... f) {\n        this.f = f;\n    }\n    \n    @AssertNoWarning(\"ExposeMutableFieldViaParameter\")\n    public void setField(TestExposeRepresentation obj, int[] f) {\n        obj.f = f;\n    }\n    \n    @AssertWarning(value=\"ExposeMutableStaticFieldViaParameter\", minScore=45)\n    public static void setHashTable(Hashtable<String, Integer> ht) {\n        TestExposeRepresentation.ht = ht;\n        System.out.println(ht);\n    }\n    \n    @AssertWarning(\"ExposeMutableFieldViaParameter\")\n    public void setPoint(Point p) {\n        this.p = p;\n    }\n    \n    public class Point {\n        public int x, y;\n\n        public int getX() {\n            return x;\n        }\n\n        public void setX(int x) {\n            this.x = x;\n        }\n\n        public int getY() {\n            return y;\n        }\n\n        public void setY(int y) {\n            this.y = y;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestFieldAccess.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicLongFieldUpdater;\nimport java.util.concurrent.atomic.AtomicReferenceFieldUpdater;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestFieldAccess {\n    @AssertWarning(\"UnusedPrivateField\")\n    private int a;\n\n    @AssertWarning(\"UnusedPrivateField\")\n    static int b;\n\n    @AssertWarning(\"UnusedPublicField\")\n    public int c;\n    \n    @AssertWarning(\"UnusedPublicField\")\n    protected static int d;\n    \n    @AssertNoWarning(\"*\")\n    public int e;\n    \n    @AssertWarning(\"UnreadPrivateField\")\n    private int f;\n    \n    @AssertWarning(\"FieldShouldBeStatic\")\n    @AssertNoWarning(\"UnreadPrivateField\")\n    private final String g = \"test\";\n    \n    @AssertNoWarning(\"FieldShouldBeStatic\")\n    @AssertWarning(value=\"UnreadPrivateField\", minScore=45)\n    private final String h; \n    \n    {\n        if(Math.random() > 0.5)\n            h = \"pass\";\n        else\n            h = \"fail\";\n    }\n    \n    @AssertWarning(\"UnreadPrivateField\")\n    public void setF(int f) {\n        this.f = f;\n    }\n    \n    @AssertWarning(value=\"UnreadPrivateField\", maxScore=40)\n    private Object refField;\n    \n    public void setRef(Object val) {\n        refField = val;\n    }\n    \n    public class SubClass extends TestFieldAccess {\n        public int getE() {\n            return e;\n        }\n\n        public void setE(int e) {\n            this.e = e;\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    static class TestGeneric<T> {\n        private T[] data;\n\n        public T[] getData() {\n            return data;\n        }\n\n        public void setData(T[] data) {\n            this.data = data;\n        }\n    }\n    \n    private double x;\n    private double y;\n    private double z;\n\n    @AssertNoWarning(\"*\")\n    private long updated;\n    \n    private static final AtomicLongFieldUpdater<TestFieldAccess> alfu = AtomicLongFieldUpdater.newUpdater(TestFieldAccess.class, \"updated\");\n    \n    public long inc() {\n        return alfu.incrementAndGet(this);\n    }\n    \n    @AssertNoWarning(\"*\")\n    private Object refUpdated;\n    \n    private static final AtomicReferenceFieldUpdater<TestFieldAccess, Object> arfu = AtomicReferenceFieldUpdater\n            .newUpdater(TestFieldAccess.class, Object.class, \"refUpdated\");\n    \n    public boolean cas(Object expected, Object updated) {\n        return arfu.compareAndSet(this, expected, updated);\n    }\n    \n    @AssertNoWarning(\"*\")\n    private long reflected;\n    \n    public void updateReflected() throws Exception {\n        Class<TestFieldAccess> clazz = TestFieldAccess.class;\n        Field field = clazz.getDeclaredField(\"reflected\");\n        field.setAccessible(true);\n        field.set(this, ((long)field.get(this))+1);\n    }\n    \n    @AssertNoWarning(\"*\")\n    private long mh;\n\n    public void updateMH() throws Throwable {\n        MethodHandle getter = MethodHandles.lookup().findGetter(TestFieldAccess.class, \"mh\", long.class);\n        MethodHandle setter = MethodHandles.lookup().findSetter(TestFieldAccess.class, \"mh\", long.class);\n        setter.invokeExact(this, (((long)getter.invokeExact(this))+1));\n    }\n\n    @AssertWarning(\"FieldUsedInSingleMethod\")\n    public void test() {\n        x = Math.random();\n        if(x > 0.5) {\n            System.out.println(\"Big!\");\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testOk() {\n        if(z > 0.5) {\n            System.out.println(\"Big!\");\n        }\n        z = Math.random();\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testOk(TestFieldAccess tfa) {\n        if(tfa.y > 0) {\n            this.y = tfa.y;\n            System.out.println(\"Big!\");\n        }\n    }\n    \n    @AssertWarning(\"UnwrittenPublicField\")\n    public long unwritten;\n    \n    @AssertWarning(\"UnwrittenPublicField\")\n    public long getUnwritten() {\n        return unwritten;\n    }\n    \n    @AssertWarning(value=\"UnwrittenPrivateField\", maxScore=30)\n    private transient long unwrittenTransient;\n    \n    @AssertWarning(\"UnwrittenPrivateField\")\n    public long getUnwrittenTransient() {\n        return unwrittenTransient;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public enum MyEnum {\n        A, B, C;\n    }\n\n    public enum MyEnum2 {\n        A, B, C;\n        \n        @AssertWarning(\"StaticFieldShouldBeFinal\")\n        public static String STATIC = \"\";\n        \n        @AssertWarning(\"MutableEnumField\")\n        public String field;\n        \n        public final List<String> list = Arrays.asList(\"a\", \"b\", \"c\");\n        \n        public void agg(String s) {\n            field += s;\n        }\n        \n        public String get(int i) {\n            return list.get(i);\n        }\n    }\n    \n    @AssertWarning(\"FieldIsAlwaysNull\")\n    private String s = null;\n    \n    public void reset() {\n        s = null;\n    }\n    \n    public String getS() {\n        return s;\n    }\n    \n    @AssertNoWarning(\"FieldIsAlwaysNull\")\n    private String s2 = Math.random() > 0.5 ? \"test\" : null;\n    \n    public void reset2() {\n        s2 = null;\n    }\n    \n    public String getS2() {\n        return s2;\n    }\n\n    @AssertWarning(\"StaticFieldShouldBeFinal\")\n    public static double VALUE = Math.random(); \n    \n    public static double getValue() {\n        return VALUE;\n    }\n    \n    @AssertWarning(\"StaticFieldShouldBeRefactoredToFinal\")\n    public static double VALUE_COMPLEX = Math.random();\n    \n    static {\n        if(VALUE_COMPLEX < 0.5)\n            VALUE_COMPLEX = Math.random();\n    }\n    \n    public static double getValueComplex() {\n        return VALUE_COMPLEX;\n    }\n    \n    @AssertWarning(value = \"StaticFieldShouldBePackagePrivate\", maxScore=50)\n    protected static double VALUE_NON_FINAL = Math.random();\n    \n    public static double getValueNonFinal() {\n        return VALUE_NON_FINAL;\n    }\n    \n    public static void recreateValueNonFinal() {\n        VALUE_NON_FINAL = Math.random();\n    }\n\n    @AssertWarning(\"StaticFieldShouldBePackagePrivate\")\n    public static final Object data = new Integer[] {5,4,3};\n\n    @AssertNoWarning(\"StaticField*\")\n    public static final Object empty = new Integer[] {};\n\n    @AssertWarning(\"StaticFieldCannotBeFinal\")\n    public static Object usedEverywhere = \"1\";\n    \n    public static void recreate() {\n        usedEverywhere = \"2\";\n    }\n    \n    @AssertWarning(\"StaticFieldShouldBeFinalAndPackagePrivate\")\n    public static double[] ARRAY = {1.0, 2.0, 3.0};\n    \n    public double getArrayElement(int x) {\n        return ARRAY[x];\n    }\n    \n    public interface FieldInterface {\n        @AssertNoWarning(\"*\")\n        public static int val = 10;\n\n        @AssertWarning(\"StaticFieldShouldBeNonInterfacePackagePrivate\")\n        public static int[] arr = {1,2,3};\n\n        @AssertWarning(\"StaticFieldMutableArray\")\n        public static int[] usedArr = {1,2,3};\n        \n        @AssertNoWarning(\"*\")\n        public static Collection<String> emptyStrings = Arrays.asList();\n\n        @AssertWarning(\"StaticFieldMutableCollection\")\n        public static Collection<String> strings = Arrays.asList(\"1\");\n\n        @AssertWarning(\"StaticFieldMutableCollection\")\n        public static Collection<String> stringsList = new ArrayList<>();\n    }\n    \n    @AssertNoWarning(\"*\")\n    private static int exposeOk = 1;\n\n    @AssertNoWarning(\"*\")\n    public static int getExposeOk() {\n        exposeOk++;\n        System.out.print(exposeOk);\n        return exposeOk;\n    }\n    \n    @AssertWarning(value=\"ExposeMutableFieldViaReturnValue\", maxScore=25)\n    private int[] expose = {1};\n    \n    @AssertWarning(\"ExposeMutableFieldViaReturnValue\")\n    public int[] getExpose() {\n        expose[0]++;\n        System.out.print(expose[0]);\n        return expose;\n    }\n    \n    public void setExpose(int[] expose) {\n        this.expose = expose;\n    }\n    \n    @AssertNoWarning(\"*\")\n    private int[] expose2 = {1};\n    \n    @AssertNoWarning(\"*\")\n    public int[] getExpose2() {\n        int[] data = expose2;\n        expose2 = new int[] {3};\n        return data;\n    }\n    \n    public void updateExpose2() {\n        expose2 = new int[] {2};\n    }\n    \n    @AssertNoWarning(\"*\")\n    public static class Reflected {\n        long x = 0;\n        long y = 0;\n        long z = 0;\n        \n        public void set(String name) throws Exception {\n            Reflected.class.getDeclaredField(name).set(this, 1L);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestFinalizer.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestFinalizer {\n    static class SuperClass {\n        @Override\n        protected void finalize() throws Throwable {\n            System.out.println(\"Finalize!!!\");\n        }\n    }\n\n    static class Nullify extends SuperClass {\n        @Override\n        @AssertWarning(value=\"FinalizeNullifiesSuper\", minScore = 40, maxScore = 60)\n        protected void finalize() throws Throwable {\n        }\n    }\n\n    static class Useless extends SuperClass {\n        @Override\n        @AssertWarning(value=\"FinalizeUselessSuper\", minScore = 30, maxScore = 50)\n        protected void finalize() throws Throwable {\n            super.finalize();\n        }\n    }\n\n    static class Useful extends SuperClass {\n        @Override\n        @AssertNoWarning(\"FinalizeUselessSuper\")\n        @AssertWarning(\"FinalizePublic\")\n        public void finalize() throws Throwable {\n            super.finalize();\n            System.out.println(\"More\");\n        }\n    }\n\n    static class Useful2 extends SuperClass {\n        @Override\n        @AssertNoWarning(\"FinalizeNoSuperCall\")\n        public void finalize() throws Throwable {\n            super.finalize();\n            System.out.println(\"More\");\n        }\n    }\n    \n    @AssertWarning(\"FinalizeInvocation\")\n    public void test() {\n        finalize();\n    }\n\n    @Override\n    @AssertWarning(value=\"FinalizeEmpty\", minScore = 20, maxScore = 40)\n    @AssertNoWarning(\"FinalizeNullifiesSuper\")\n    protected void finalize() {\n    }\n\n    static class FinalFinalizer {\n        @Override\n        @AssertNoWarning(\"Finalize*\")\n        protected final void finalize() {\n        }\n    }\n\n    static class NullFields {\n        InputStream is = new ByteArrayInputStream(new byte[1]);\n\n        @AssertWarning(\"FinalizeNullsFields\")\n        @Override\n        protected void finalize() throws Throwable {\n            System.out.println(\"Finalizer\");\n            is = null;\n        }\n    }\n\n    static class NullFieldsOnly {\n        InputStream is = new ByteArrayInputStream(new byte[1]);\n        Object obj = new Object();\n\n        @AssertWarning(\"FinalizeOnlyNullsFields\")\n        @Override\n        protected void finalize() throws Throwable {\n            is = null;\n            obj = null;\n        }\n    }\n\n    static class NoSuperCall extends SuperClass {\n        @AssertWarning(\"FinalizeNoSuperCall\")\n        @Override\n        protected void finalize() throws Throwable {\n            if (Math.random() > 0.5) {\n                System.out.println(\"Mwahaha\");\n            } else {\n                System.out.println(\"Hohoho\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestFloatComparison.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestFloatComparison {\n    @AssertWarning(value=\"FloatComparison\", minScore = 25, maxScore = 45)\n    void testFloat(float a, float b) {\n        if(a == b)\n            System.out.println(\"Equals\");\n    }\n\n    @AssertWarning(value=\"FloatComparison\", minScore = 5, maxScore = 20)\n    void testFloatInt(float a, int b) {\n        if(a == b)\n            System.out.println(\"Equals\");\n    }\n    \n    @AssertNoWarning(\"FloatComparison\")\n    void testFloat(float a) {\n        if(a == 0)\n            System.out.println(\"Equals\");\n    }\n\n    @AssertWarning(value=\"FloatComparison\", minScore = 25, maxScore = 45)\n    void testDouble(double a, double b) {\n        if(a == b)\n            System.out.println(\"Equals\");\n    }\n    \n    @AssertNoWarning(\"*\")\n    void testRound(double a) {\n        if(a == (int)a)\n            System.out.println(\"Round\");\n    }\n    \n    @AssertNoWarning(\"FloatComparison\")\n    void testDouble(double a) {\n        if(a == 0)\n            System.out.println(\"Equals\");\n    }\n    \n    @AssertWarning(value=\"FloatComparison\", minScore = 5, maxScore = 15)\n    void testDouble2(double a) {\n        if(a == 3.0)\n            System.out.println(\"Equals\");\n    }\n    \n    @AssertWarning(value=\"FloatComparison\", minScore = 15, maxScore = 25)\n    void testDouble3(double a) {\n        if(a == 5.0)\n            System.out.println(\"Equals\");\n        if(a == 1000)\n            System.out.println(\"Equals\");\n    }\n    \n    @AssertWarning(value=\"FloatComparison\", minScore = 15, maxScore = 25)\n    void testDouble4(double a) {\n        if(a == 10.5)\n            System.out.println(\"Equals\");\n    }\n\n    @AssertWarning(value=\"FloatComparison\", minScore = 10, maxScore = 20)\n    void testDoublePhi(boolean b, double a) {\n        double x = a;\n        if(b) x = a+10;\n        if(a == x)\n            System.out.println(\"Equals\");\n    }\n    \n    @AssertWarning(value=\"FloatComparison\", minScore = 10, maxScore = 20)\n    void testDoubleTernary(boolean b, double a) {\n        double x = b ? a : a+10;\n        if(a == x)\n            System.out.println(\"Equals\");\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestFloatNaN.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestFloatNaN {\n    @AssertWarning(\"UnusedLocalVariable\")\n    void testLambda() {\n        Runnable r = () -> System.out.println();\n        Runnable r2 = System.out::println;\n    }\n    \n    @AssertWarning(\"FloatCompareToNaN\")\n    void testFloat(float a) {\n        if(a == Float.NaN)\n            System.out.println(\"NaN!\");\n    }\n\n    @AssertWarning(\"FloatCompareToNaN\")\n    void testNotFloat(float a) {\n        if(Float.NaN != a)\n            System.out.println(\"NaN!\");\n    }\n    \n    @AssertWarning(\"FloatCompareToNaN\")\n    void testDouble(double a) {\n        if(a == Double.NaN)\n            System.out.println(\"NaN!\");\n    }\n    \n    @AssertWarning(\"FloatCompareToNaN\")\n    void testNotDouble(double a) {\n        if(Double.NaN != a)\n            System.out.println(\"NaN!\");\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestIgnoredException.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestIgnoredException {\n    @AssertWarning(\"IgnoredException\")\n    public void test1() {\n        try {\n            System.out.println(\"Test\");\n        }\n        catch(Exception ex) {\n            \n        }\n    }\n\n    @AssertNoWarning(\"IgnoredException\")\n    public Exception testOk() {\n        try {\n            System.out.println(\"Test\");\n        }\n        catch(Exception ex) {\n            return ex;\n        }\n        return null;\n    }\n    \n    @AssertWarning(\"IgnoredException\")\n    public void test2() {\n        try {\n            System.out.println(\"Test\");\n        }\n        catch(Throwable t) {\n            return;\n        }\n        System.out.println(\"Passed\");\n    }\n\n    @AssertWarning(\"IgnoredException\")\n    public void test3() {\n        for(int i=0; i<10; i++) {\n            try {\n                System.out.println(\"Test\");\n            }\n            catch(RuntimeException ex) {\n                continue;\n            }\n        }\n    }\n\n    @AssertWarning(\"IgnoredException\")\n    public Object test4() {\n        try {\n            System.out.println(\"Test\");\n        }\n        catch(Throwable t) {\n            return \"xyz\".trim();\n        }\n        System.out.println(\"Passed\");\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestIncorrectVarArg.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Arrays;\nimport java.util.stream.IntStream;\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestIncorrectVarArg {\n    @AssertWarning(\"PrimitiveArrayPassedAsVarArg\")\n    public void testAsList(int[] data) {\n        Arrays.asList(data).forEach(System.out::println);\n    }\n    \n    private void custom(Object... data) {\n        System.out.println(data.length);\n    }\n\n    private void noVarArg(Object[] data) {\n        System.out.println(data.length);\n    }\n    \n    @AssertWarning(\"PrimitiveArrayPassedAsVarArg\")\n    public void testCustom(int[] data) {\n        custom(data);\n    }\n\n    @AssertNoWarning(\"PrimitiveArrayPassedAsVarArg\")\n    public void testOk(int[] data) {\n        noVarArg(new Object[] {data});\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testIntStream(int[] data) {\n        IntStream.of(data).forEach(System.out::println);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestInfiniteLoop.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestInfiniteLoop {\n    @AssertWarning(\"InfiniteLoop\")\n    public void testLoop(int x) {\n        while (x > 2) {\n            System.out.println(x);\n        }\n    }\n\n    @AssertWarning(\"InfiniteLoop\")\n    public void testFlag() {\n        boolean b = true;\n        while (b) {\n            System.out.println(b);\n        }\n    }\n\n    @AssertNoWarning(\"InfiniteLoop\")\n    @AssertWarning(\"InvariantLoopCondition\")\n    public void testLoopInvariant(int x) {\n        while (x > 2) {\n            System.out.println(1);\n            if (Math.random() > 0.5)\n                break;\n        }\n    }\n\n    @AssertNoWarning(\"InfiniteLoop\")\n    public void testLoopOk(int x) {\n        while (x > 2) {\n            System.out.println(x--);\n        }\n    }\n\n    @AssertNoWarning(\"InfiniteLoop\")\n    @AssertWarning(\"InvariantLoopConditionPart\")\n    public void testLoopPart(int x, int z) {\n        while (x > 2 || z == 10) {\n            System.out.println(x--);\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testLoopPartOk(int x, int y, boolean b) {\n        while (x > 2 || b && y > 10) {\n            System.out.println(x--);\n            System.out.println(y--);\n        }\n    }\n\n    TestInfiniteLoop next;\n\n    @AssertNoWarning(\"InfiniteLoop\")\n    public void dump(TestInfiniteLoop obj) {\n        int x = 10;\n        while (obj != null) {\n            --x;\n            obj = obj.next;\n        }\n        System.out.println(x);\n    }\n\n    @AssertNoWarning(\"*\")\n    public void dump(int[] data) {\n        for(int i=0; data != null && i < data.length; i++) {\n            System.out.println(data[i]);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestInfiniteRecursion.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.List;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestInfiniteRecursion {\n    @AssertWarning(\"InfiniteRecursion\")\n    public TestInfiniteRecursion(int x) {\n        System.out.print(new TestInfiniteRecursion(1));\n    }\n    \n    @AssertWarning(\"InfiniteRecursion\")\n    public TestInfiniteRecursion(int x, int y) {\n        if(x > 2)\n            return;\n        new TestInfiniteRecursion(x, y);\n    }\n    \n    @AssertWarning(\"InfiniteRecursion\")\n    public static void testSimple() {\n        testSimple();\n    }\n\n    @AssertWarning(\"InfiniteRecursion\")\n    public static void testSimple(int x) {\n        testSimple(x);\n    }\n    \n    @AssertWarning(\"InfiniteRecursion\")\n    public static void testSimpleMod(int x) {\n        x--;\n        testSimpleMod(x);\n    }\n\n    @AssertWarning(\"InfiniteRecursion\")\n    public static void testSimpleCheck(int x) {\n        if(x > 2)\n            return;\n        testSimpleCheck(x);\n    }\n    \n    @AssertWarning(\"InfiniteRecursion\")\n    public static void testSimpleCheck(int x, long y, double z, boolean b) {\n        if(!b)\n            return;\n        testSimpleCheck(x, y, z, b);\n    }\n    \n    @AssertNoWarning(\"InfiniteRecursion\")\n    public static void testSimpleModOk(int x) {\n        if(x-- > 0)\n            return;\n        testSimpleModOk(x);\n    }\n    \n    @AssertWarning(\"InfiniteRecursion\")\n    public int test() {\n        return test();\n    }\n    \n    int f;\n    \n    private boolean updateF() {\n        return --f > 0;\n    }\n    \n    @AssertNoWarning(\"InfiniteRecursion\")\n    public boolean has() {\n        if(updateF())\n            return false;\n        return has();\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void printRecursively(List<?> list) {\n        for(Object obj : list) {\n            if(obj instanceof List) {\n                printRecursively((List<?>)obj);\n            } else\n                System.out.println(obj);\n        }\n    }\n    \n    public interface Iface {\n        @AssertWarning(\"InfiniteRecursion\")\n        default public int test(int x) {\n            return test(x+1);\n        }\n    }\n    \n    public static abstract class InfiniteLoop {\n        int x;\n\n        @AssertWarning(\"InfiniteRecursion\")\n        void report() {\n            report();\n        }\n\n        @AssertWarning(\"InfiniteRecursion\")\n        void report2(Object a, Object b) {\n            if (a.equals(b)) // we miss this one because we assume equals can do\n                             // a store\n                report2(a, b);\n        }\n\n        @AssertWarning(\"InfiniteRecursion\")\n        static void report3(InfiniteLoop obj) {\n            InfiniteLoop.report3(obj);\n        }\n\n        @AssertNoWarning(\"InfiniteRecursion\")\n        void doNotReport(Object a, Object b) {\n            if (a.equals(b)) {\n                doNotReport(b, a);\n            }\n        }\n\n        @AssertNoWarning(\"InfiniteRecursion\")\n        void doNotReport2(Object a, Object b) {\n            if (x == 0) {\n                x = 1;\n                // A field has been checked and modified\n                doNotReport2(a, b);\n            }\n        }\n\n        @AssertNoWarning(\"InfiniteRecursion\")\n        void doNotReport3(Object a, Object b) {\n            if (opaque()) {\n                // Assume method invocation reads and writes all fields\n                doNotReport3(a, b);\n            }\n        }\n\n        protected abstract boolean opaque();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestInitializerRefersSubclass.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\n@AssertWarning(\"InitializerRefersSubclass\")\npublic class TestInitializerRefersSubclass {\n    static final TestInitializerRefersSubclass instance = new SubClass();\n    \n    public static class SubClass extends TestInitializerRefersSubclass {\n        \n    }\n    \n    @AssertNoWarning(\"InitializerRefersSubclass\")\n    static class AnonymousOk {\n        static final AnonymousOk instance = new AnonymousOk() {};\n    }\n    \n    @AssertWarning(\"InitializerRefersSubclass\")\n    static class Anonymous {\n        static final AnonymousSubClass instance = new AnonymousSubClass() {};\n    }\n    \n    static class AnonymousSubClass extends Anonymous {\n        \n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestInternationalization.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.OutputStream;\nimport java.io.PrintWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Locale;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestInternationalization {\n    @AssertWarning(\"ConvertCaseWithDefaultLocale\") \n    public String toLowerCase(String s) {\n        return s.toLowerCase();\n    }\n\n    @AssertWarning(\"ConvertCaseWithDefaultLocale\") \n    public String toUpperCase(String s) {\n        return s.toUpperCase();\n    }\n\n    @AssertNoWarning(\"*\") \n    public String toLowerCaseOk(String s) {\n        return s.toLowerCase(Locale.ENGLISH);\n    }\n    \n    @AssertNoWarning(\"*\") \n    public String toUpperCaseOk(String s) {\n        return s.toUpperCase(Locale.ENGLISH);\n    }\n\n    @AssertWarning(value=\"MethodReliesOnDefaultEncoding\", minScore = 40, maxScore = 45) \n    public byte[] toBytes(String s) {\n        return s.getBytes();\n    }\n    \n    @AssertWarning(value=\"MethodReliesOnDefaultEncoding\", minScore = 28, maxScore = 35) \n    public PrintWriter printWriter(OutputStream os) {\n        return new PrintWriter(os);\n    }\n\n    @AssertNoWarning(\"*\") \n    public PrintWriter printWriter() {\n        return new PrintWriter(System.out);\n    }\n    \n    @AssertNoWarning(\"*\") \n    public byte[] toBytesOk(String s) {\n        return s.getBytes(StandardCharsets.UTF_8);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestInvalidMinMax.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestInvalidMinMax {\n    @AssertWarning(\"InvalidMinMax\")\n    public int checkBounds(int rawInput) {\n        return Math.min(0, Math.max(100, rawInput));\n    }\n\n    @AssertWarning(\"InvalidMinMax\")\n    public int checkBounds2(int rawInput) {\n        return Math.min(0, Math.max(rawInput, 100));\n    }\n\n    @AssertWarning(\"InvalidMinMax\")\n    public int checkBounds3(int rawInput) {\n        return Math.min(Math.max(rawInput, 100), 0);\n    }\n\n    @AssertWarning(\"InvalidMinMax\")\n    public int checkBounds4(int rawInput) {\n        return Math.min(Math.max(100, rawInput), 0);\n    }\n\n    @AssertWarning(\"InvalidMinMax\")\n    public int checkBounds5(int rawInput) {\n        return Math.max(Math.min(0, rawInput), 100);\n    }\n\n    @AssertWarning(\"InvalidMinMax\")\n    public int checkBounds6(int rawInput) {\n        rawInput = Math.min(0, rawInput);\n        rawInput = Math.max(100, rawInput);\n        return rawInput;\n    }\n\n    @AssertWarning(\"InvalidMinMax\")\n    public int checkWithVars(int rawInput) {\n        int min = 0;\n        int max = 100;\n        if(rawInput > 50) {\n            System.out.println(\"Phew...\");\n        }\n        rawInput = Math.min(min, rawInput);\n        rawInput = Math.max(max, rawInput);\n        return rawInput;\n    }\n    \n    @AssertWarning(\"InvalidMinMax\")\n    public int getScore(int totalCount, int failCount, double scaleFactor) {\n        // Based on\n        // https://github.com/marksinclair/junit-plugin/commit/c0dc11e08923edd23cee90962da638e4a7eb47d5\n        int score = (totalCount == 0) ? 100 : (int) (100.0 * Math.max(1.0, Math.min(0.0, 1.0\n            - (scaleFactor * failCount) / totalCount)));\n        return score;\n    }\n\n    @AssertWarning(\"InvalidMinMax\")\n    public long checkBounds(long rawInput) {\n        return Math.min(0, Math.max(100, rawInput));\n    }\n\n    @AssertWarning(\"InvalidMinMax\")\n    public float checkBounds(float rawInput) {\n        return Math.min(0, Math.max(100, rawInput));\n    }\n\n    @AssertWarning(\"InvalidMinMax\")\n    public double checkBounds(double rawInput) {\n        return Math.min(0, Math.max(100, rawInput));\n    }\n\n    @AssertNoWarning(\"InvalidMinMax\")\n    public int checkBoundsCorrect(int rawInput) {\n        return Math.min(100, Math.max(0, rawInput));\n    }\n\n    @AssertNoWarning(\"InvalidMinMax\")\n    public int checkBoundsCorrect2(int rawInput) {\n        return Math.max(0, Math.min(100, rawInput));\n    }\n\n    @AssertNoWarning(\"InvalidMinMax\")\n    public int checkBoundsCorrect3(int rawInput) {\n        rawInput = Math.min(100, rawInput);\n        rawInput = Math.max(0, rawInput);\n        return rawInput;\n    }\n\n    @AssertNoWarning(\"InvalidMinMax\")\n    public long checkBoundsCorrect(long rawInput) {\n        return Math.min(100, Math.max(0, rawInput));\n    }\n\n    @AssertNoWarning(\"InvalidMinMax\")\n    public float checkBoundsCorrect(float rawInput) {\n        return Math.min(100, Math.max(0, rawInput));\n    }\n\n    @AssertNoWarning(\"InvalidMinMax\")\n    public double checkBoundsCorrect(double rawInput) {\n        return Math.min(100, Math.max(0, rawInput));\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestIteratorContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestIteratorContract implements Iterator<String> {\n    Iterator<String> input = Arrays.asList(\"foo\", \"bar\").iterator();\n    String nextElement;\n    \n    @Override\n    @AssertWarning(\"IteratorHasNextCallsNext\")\n    public boolean hasNext() {\n        try {\n            nextElement = next();\n        } catch (NoSuchElementException e) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    @AssertNoWarning(\"*\")\n    public String next() {\n        return nextElement == null ? input.next() : nextElement;\n    }\n    \n    public class Iterator2 implements Iterator<String> {\n        int state = 0;\n\n        @Override\n        public boolean hasNext() {\n            return state == 0;\n        }\n\n        @Override\n        @AssertWarning(\"IteratorNoThrow\")\n        public String next() {\n            return ++state == 1 ? \"Hello!\".toLowerCase() : null;\n        }\n        \n    }\n    \n    @AssertNoWarning(\"*\")\n    public class NonIterator {\n        public boolean hasNext() {\n            return next() == null;\n        }\n\n        public String next() {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestJcipProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\nimport net.jcip.annotations.Immutable;\n\n/**\n * @author lan\n *\n */\n@Immutable\npublic class TestJcipProblems {\n    @AssertWarning(\"NonFinalFieldInImmutableClass\")\n    private int a;\n\n    @AssertNoWarning(\"NonFinalFieldInImmutableClass\")\n    private transient int b;\n    \n    @AssertNoWarning(\"NonFinalFieldInImmutableClass\")\n    private volatile int c;\n    \n    @AssertNoWarning(\"NonFinalFieldInImmutableClass\")\n    private final int d = 1;\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestKnownComparison.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.concurrent.ThreadLocalRandom;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestKnownComparison {\n    private static Integer val = 0; \n    private Integer f = 0;\n    private final int finalField;\n    private char ch;\n    private boolean flag;\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testFinalField() {\n        if(finalField > 15) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testFinalFieldOutside() {\n        if(finalField < 15) {\n            System.out.println(\"Always!\");\n        }\n        System.out.println(\"Outside!\");\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testFinalFieldReturn() {\n        if(finalField < 15) {\n            System.out.println(\"Always!\");\n            return;\n        }\n        System.out.println(\"Outside!\");\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testCompoundStore() {\n        int a, b;\n        a = b = 0;\n        if(a > 15) {\n            System.out.println(b);\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public TestKnownComparison() {\n        finalField = 10;\n        if(Math.random() > 0.5) {\n            flag = true;\n        }\n        testInstanceFieldOk2(true);\n        if(finalField != 10) {\n            System.out.println(\"Never!\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testSideEffectFree() {\n        f = 10;\n        int x = getFinalField();\n        if(f != 10) {\n            System.out.println(\"Never: \"+x);\n        }\n    }\n    \n    private int getFinalField() {\n        return finalField;\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public TestKnownComparison(boolean b) {\n        if(ch + 1 == 5) {\n            System.out.println(\"Never! \"+b);\n        }\n        finalField = 10;\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testBooleanStringCompare() {\n        boolean b = false;\n        String s = \"false\";\n        if(String.valueOf(b).equals(s)) {\n            System.out.println(\"Always!\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testTernaryConstCompare(int a) {\n        int x = a == 5 ? a : 5;\n        if (x == 5) {\n            System.out.println(\"Ok\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testBooleanAssignment(int a, int b) {\n        boolean val = a == 5 && b > 10;\n        if(val && a == 5) {\n            System.out.println(\"ok\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testBooleanAssignmentNot(int a, int b) {\n        boolean val = a != 5 || b > 10;\n        if(!val && a != 5) {\n            System.out.println(\"ok\");\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testBooleanAssignmentTernary(int a, int b) {\n        boolean val = a > 5 ? b > 10 : b < 10;\n        if(val && a == 5) {\n            System.out.println(\"ok\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testIfConstCompare(int a) {\n        if (a == 5) {\n            System.out.println(\"A is 5\");\n            if (a > 6) {\n                System.out.println(\"Cannot be\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testIfConstNotCompare(int a, int b) {\n        if (a != 5 || b != 10) {\n            System.out.println(\"A is 5 and B is 10\");\n        } else {\n            if (a > 6) {\n                System.out.println(\"Cannot be\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testIfConstStrings(String x) {\n        if (x.equals(\"123\")) {\n            if (x.length() == 4) {\n                System.out.println(\"Cannot be\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testIfConstStringsNot(String x) {\n        if (!x.equals(\"123\") || Math.random() > 0.5) {\n            System.out.println(\"x is not 123 or random is big\");\n        } else {\n            if (x.length() == 4) {\n                System.out.println(\"Cannot be\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testTernaryConstCompare2(int a) {\n        int x = a != 5 ? 7 : a + 2;\n        if (x == 7) {\n            System.out.println(\"Ok\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testIntegerStringCompare() {\n        int x = 100;\n        String s = \"100\";\n        if(String.valueOf(x).equals(s)) {\n            System.out.println(\"Always!\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testBooleanBoxingCompare() {\n        boolean b = false;\n        Boolean boxed = b;\n        if(boxed.equals(false)) {\n            System.out.println(\"Always!\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testAnd(int x) {\n        int a = 2;\n        int b = 3;\n        if (a > b && x > 2) {\n            System.out.println(\"Never ever!\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testArrayLength(boolean x) {\n        int[] a = {1, 2, 3};\n        int[] b = {4, 5, 6};\n        int[] c = x ? a : b;\n        if (c.length > 5) {\n            System.out.println(\"Never ever!\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testAnd2(int x) {\n        int a = 2;\n        int b = 3;\n        if (x > 2 && a > b) {\n            System.out.println(\"Never ever!\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void test() {\n        int a = 2;\n        int b = 3;\n        if (a < b) {\n            System.out.println(\"Why not?\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testOr(int x) {\n        int a = 2;\n        int b = 3;\n        if (x > 3 || a < b) {\n            System.out.println(\"Why not?\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testInc() {\n        int a = 2;\n        int b = 3;\n        b++;\n        if (a < b) {\n            System.out.println(\"Why not?\");\n        }\n    }\n\n    @AssertNoWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testIncrement(boolean f) {\n        int a = 2;\n        int b = 3;\n        if (f)\n            a += 2;\n        if (a < b) {\n            System.out.println(\"Why not?\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public int testTernary() {\n        int a = 2;\n        return a == 2 ? 1 : -1;\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public int testLoopBreak() {\n        int a = 2;\n        int b = 1;\n        while (true) {\n            b = 2;\n            if (++a > 4)\n                break;\n            System.out.println(\"Iteration!\");\n        }\n        return b == 2 ? 1 : -1;\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testFor() {\n        for (int i = 0; i < 10; i++) {\n            if (i == 0)\n                System.out.println(\"First!\");\n            System.out.println(\"Iteration!\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testSubFor(List<String> l2) {\n        for (int i = 0, n = l2.size(); i < n; i++) {\n            if (i - 1 == 0)\n                System.out.println(\"First!\");\n            System.out.println(\"Iteration!\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testForContinue(boolean b) {\n        for (int i = 0; i < 10; i++) {\n            if (b && i == 0 || !b && i == 2) {\n                System.out.println(\"First!\");\n            } else\n                continue;\n            System.out.println(\"Iteration!\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testNestedForForSwitch(boolean b) {\n        System.out.println(b);\n        for (int pass = 0; pass < 2; pass++) {\n            if (pass == 1)\n                System.out.println(\"Second pass\");\n            double start = 1;\n            for (double x = start; x < 1024; x *= 2) {\n                double q = x * 2;\n                switch (pass) {\n                case 0:\n                    System.out.println(q);\n                    if (x < 10) {\n                        System.out.println(\"Loop\");\n                        for (int z = 0; z < 10; z++)\n                            System.out.println(z);\n                    }\n                    System.out.println(\"Continue\");\n                    continue;\n                case 1:\n                    if (x > 128)\n                        continue;\n                    System.out.println(x + \"1\");\n                    break;\n                }\n            }\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testNestedForFor() {\n        for (int a = 0; a < 10; a++) {\n            if (a % 2 == 0) {\n                for (int iter = 0; iter < 10; iter++) {\n                    System.out.println(\"test\");\n                    if (iter > 3)\n                        continue;\n                    for (int b = a; b < 10; b++) {\n                        if (b % 2 == 0) {\n                            for (int c = (a == b) ? iter + 3 : 1; c < 20; c++) {\n                                if (a == b) {\n                                    System.out.println(\"Equal\");\n                                }\n                                System.out.println(\"InnerInner\");\n                            }\n                            System.out.println(\"Inner\");\n                        }\n                    }\n                }\n                System.out.println(\"Outer\");\n            }\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testIncInLoop() {\n        int x = 10;\n        for (int i = 0; i < 10; i++) {\n            x -= 4;\n            if (x > 0)\n                System.out.println(\"X!\");\n            System.out.println(\"Iteration!\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testInLambda(int x) {\n        Runnable r = () -> {\n            int a = 2;\n            int b = 3;\n            if (a > b && x > 2) {\n                System.out.println(\"Never ever!\");\n            }\n        };\n        r.run();\n    }\n\n    @AssertNoWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testInLambdaFP(int x) {\n        Runnable r = () -> {\n            int a = 2;\n            if (x > a) {\n                System.out.println(\"Can be\");\n            }\n        };\n        r.run();\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testComplexLoop(int x) {\n        int s = -1;\n        for (int i = 0; i < x; i++) {\n            if (i < 3) {\n                System.out.println(1);\n                if (s < 0)\n                    continue;\n            }\n            if (i < 5) {\n                System.out.println(2);\n                if (s < 0)\n                    s = i;\n                continue;\n            } else if (s < 0)\n                continue;\n            System.out.println(3);\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testComplexLoop(String ssType, String type) {\n        if (ssType == null)\n            return;\n        int istart = -1;\n        for (int i = 0; i < 100; i++) {\n            if (i == 1) {\n                System.out.println(0);\n            } else {\n                System.out.println(1);\n            }\n            if (type.equals(\"test\")) {\n                System.out.println(2);\n                --i;\n                if (istart < 0)\n                    continue;\n            } else if (type.equals(\"test2\")) {\n                System.out.println(3);\n                if (istart < 0)\n                    istart = i;\n                continue;\n            } else if (istart < 0) {\n                continue;\n            }\n            if (type.equals(\"test3\")) {\n                System.out.println(4);\n                if (i >= 0 && i <= 10)\n                    continue;\n                System.out.println(5);\n            }\n            System.out.println(6);\n            istart = -1;\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testComplexLoop2(String type) {\n        int x = -1;\n        for (int i = 0; i < 100; i++) {\n            if (type.equals(\"test\")) {\n                System.out.println(2);\n                if (x < 0)\n                    continue;\n            } else if (type.equals(\"test2\")) {\n                System.out.println(3);\n                if (x < 0)\n                    x = i;\n                continue;\n            }\n            x = -1;\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testComplexLoop3(String type) {\n        int x = -1;\n        for (int i = 0; i < 100; i++) {\n            if (type.equals(\"test\")) {\n                System.out.println(2);\n                if (x < 0)\n                    continue;\n            } else if (type.equals(\"test2\")) {\n                x = 0;\n                System.out.println(3);\n                if (x < 0)\n                    x = i;\n                continue;\n            }\n            x = -1;\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testAssert(String type) {\n        int x = 1;\n        assert x > 0 && !type.isEmpty();\n        System.out.println(x+\":\"+type);\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public static void testStaticField() {\n        val = 3;\n        if(val > 3) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public static void testStaticFieldOk() {\n        val = 2;\n        testStaticField();\n        if(val > 2) {\n            System.out.println(\"Never\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testInstanceField() {\n        f = 3;\n        if(f > 3) {\n            System.out.println(\"Never\");\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testInstanceFieldOk() {\n        f = 2;\n        testInstanceField();\n        if(f > 2) {\n            System.out.println(\"Never\");\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testInstanceFieldOk2(boolean b) {\n        f = 2;\n        int x = 0;\n        if(b) x = f++;\n        if(f > 2) {\n            System.out.println(\"Never\");\n        }\n        System.out.println(x);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testInstanceFieldOk3(int x) {\n        if(x > 0)\n            f = 2;\n        if(f > 2) {\n            System.out.println(\"Never\");\n        }\n        System.out.println(x);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testFieldLoop() {\n        f = 0;\n        while(f < 10) {\n            if(Math.random() > 0.5) {\n                f++;\n                System.out.println(f);\n            } else {\n                f = 1;\n                System.out.println(f+\"!\");\n            }\n        }\n    }\n    \n    private int[] getArray() {\n        return new int[10];\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testArray(boolean b) {\n        int x = 0;\n        if(b)\n            x = getArray().length;\n        if(x > 0) {\n            System.out.println(\"a\");\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    @AssertNoWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    static class TestInitial {\n        static long x = 2;\n        \n        static {\n            if(x < 1) {\n                System.out.println(\"Cannot be\");\n            }\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public static class TestFieldAssignment {\n        private int x = 1;\n        \n        public TestFieldAssignment(String s) {\n            this();\n            try {\n                x = Integer.parseInt(s);\n            } catch (NumberFormatException e) {\n                e.printStackTrace();\n            }\n        }\n        \n        public TestFieldAssignment() {\n        }\n\n        public void test() {\n            if(x > 0) {\n                System.out.println(\"!!!\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testAioobe(int[] x, int y) {\n        int a = 0;\n        try {\n            a = x[y];\n            if(a > 0) {\n                a = 0;\n            }\n        }\n        catch(IndexOutOfBoundsException ex) {\n            if(a > 0) {\n                a = 0;\n            }\n        }\n        System.out.println(a);\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testFieldDoWhile() {\n        do {\n            if(f == 5) {\n                System.out.println(f);\n            }\n            doSomeProcessing();\n            f = 5;\n        } while(ch < 10);\n    }\n    \n    private void doSomeProcessing() {\n        f++;\n        ch--;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testBooleanFlag() {\n        if(flag) {\n            System.out.println(\"a\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testCompareToKnown(int a, int b) {\n        if(a == 5 && b == a) {\n            System.out.println(b == 5 ? 1 : 2);\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnownDeadCode\")\n    public void testWhileExitCondition(int i) {\n        while(i != 10) {\n            System.out.println(i++);\n        }\n        if(i != 10) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testNestedSwitch(int i, int j) {\n        switch(i) {\n        case 1:\n            switch(j) {\n            case 2:\n                System.out.println(\"1-2\");\n                break;\n            }\n            break;\n        case 2:\n            switch(j) {\n            case 3:\n                if(i == 2)\n                    System.out.println(\"2-3\");\n                break;\n            }\n            break;\n        case 3:\n            return;\n        case 4:\n            return;\n        default:\n            break;\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public static void catchInfiniteLoop(int[] arr, int offset) {\n        while (true) {\n            try {\n                arr[offset++] = 1;\n                break;\n            } catch (IndexOutOfBoundsException ex) {\n                System.out.println(\"Retrying...\");\n                offset = 0;\n            }\n        }\n        System.out.println(\"done\");\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testSwitchPassthru(int x) {\n        int a = 3;\n        for(int i=0; i<10; i++) {\n            switch(x) {\n            case 1:\n                System.out.println(x);\n                a = 2;\n                if(Math.random() > 0.5)\n                    return;\n            case 2:\n                if(a == 3) {\n                    System.out.println(\"Always\");\n                }\n                break;\n            default:\n                System.out.println(\"Default\");\n            }\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testSwitchMisplacedDefault(int x) {\n        int a = 3;\n        switch(x) {\n        case 3:\n            System.out.println(\"3\");\n            break;\n        default:\n            System.out.println(\"Default\");\n            a = 2;\n        case 1:\n        case 2:\n            if(a == 3) {\n                System.out.println(\"Possible\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testSwitchMisplacedDefault2(int x) {\n        int a = 3;\n        switch(x) {\n        default:\n        case 3:\n            System.out.println(\"3\");\n            a = 2;\n            break;\n        case 1:\n        case 2:\n            if(a == 3) {\n                System.out.println(\"Always\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testCatch(String s) {\n        try {\n            if(s.equals(\"test\")) {\n                throw new IOException();\n            }\n            System.out.println(s);\n        }\n        catch(IOException ex) {\n            if(s.equals(\"test\")) {\n                System.out.println(\"Always!\");\n            }\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testOrNested(int x) {\n        if(Math.random() > 0.5 && (x == 1 || x == 2)) {\n            if(x == 1) {\n                System.out.println(2);\n            } else {\n                System.out.println(1);\n            }\n            System.out.println(\"ok\");\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    static class ArrayTest {\n        int[] arr;\n        \n        public ArrayTest(boolean b) {\n            // such construction is incorrectly handled by procyon\n            // arr = b ? new int[] {1,2} : new int[] {1};\n            if(b) {\n                arr = new int[] {1,2};\n            } else {\n                arr = new int[] {1};\n            }\n        }\n        \n        public void testArrayLength() {\n            if(arr.length > 1) {\n                System.out.println(\"Big\");\n            }\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testFinally() {\n        int i = 0;\n        try {\n            i = ThreadLocalRandom.current().nextInt();\n        }\n        finally {\n            try {\n                System.out.println(\"Finish\");\n            }\n            finally {\n                if(i == 5) {\n                    System.out.println(\"Hello\");\n                }\n            }\n        }\n    }\n\n    // procyon does not perfectly merge these finally blocks \n    //@AssertNoWarning(\"*\")\n    public void testNestedFinally() {\n        int i = 0;\n        try {\n            i = ThreadLocalRandom.current().nextInt();\n        } catch(Throwable t) {\n            t.printStackTrace();\n        }\n        finally {\n            try {\n                System.out.println(\"Finish\");\n            }\n            finally {\n                if(i == 5) {\n                    System.out.println(\"Hello\");\n                }\n            }\n        }\n    }\n    \n    @AssertWarning(\"ResultOfComparisonIsStaticallyKnown\")\n    public void testSystemExit(int x) {\n        if(x != 5) {\n            System.out.println(\"Oops\");\n            System.exit(0);\n        }\n        if(x == 5) {\n            System.out.println(\"Always\");\n        }\n    }\n\n    // procyon bug\n//    @AssertNoWarning(\"*\")\n//    public int testRetries(Scanner sc) {\n//        int cnt = 0;\n//        while(true) {\n//            try {\n//                return sc.nextInt();\n//            }\n//            catch(InputMismatchException ime) {\n//                if(cnt < 20) {\n//                    cnt++;\n//                } else {\n//                    throw ime;\n//                }\n//            }\n//        }\n//    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestLockProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.locks.Condition;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestLockProblems {\n    @AssertWarning(\"IncorrectConcurrentMethod\")\n    public void waitForCondition(Condition cond) throws InterruptedException {\n        cond.wait(1);\n    }\n\n    @AssertWarning(\"IncorrectConcurrentMethod\")\n    public void notifyCondition(Condition cond) {\n        cond.notify();\n    }\n\n    @AssertNoWarning(\"IncorrectConcurrentMethod\")\n    public void waitForObject(Object cond) throws InterruptedException {\n        cond.wait();\n    }\n\n    @AssertNoWarning(\"*\")\n    public void waitForConditionOk(Condition cond) throws InterruptedException {\n        cond.await();\n    }\n\n    @AssertWarning(\"IncorrectConcurrentMethod\")\n    public void notifyCountDown(CountDownLatch latch) {\n        latch.notifyAll();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestMinValueHandling.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.SplittableRandom;\nimport java.util.concurrent.ThreadLocalRandom;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestMinValueHandling {\n    @AssertWarning(value=\"AbsoluteValueOfRandomInt\", minScore=50, maxScore=60)\n    public int testRandom() {\n        int h = ThreadLocalRandom.current().nextInt();\n        int v = Math.abs(h);\n        return v % 15;\n    }\n\n    @AssertWarning(value=\"AbsoluteValueOfRandomInt\", minScore=30, maxScore=40)\n    public long testRandomLong() {\n        synchronized(this) {\n            long h = new SplittableRandom().nextLong();\n            long v = Math.abs(h);\n            return v % 15;\n        }\n    }\n    \n    @AssertWarning(value=\"AbsoluteValueOfHashCode\", minScore=55, maxScore=60)\n    public int testHashCodeRem(Object obj) {\n        int h = obj.hashCode();\n        int v = Math.abs(h);\n        return v % 15;\n    }\n    \n    @AssertWarning(value=\"AbsoluteValueOfHashCode\", minScore=15, maxScore=25)\n    public int testPowerOf2Rem(Object obj) {\n        int h = obj.hashCode();\n        int v = Math.abs(h);\n        return v % 32;\n    }\n    \n    @AssertWarning(value=\"AbsoluteValueOfHashCode\", minScore=45, maxScore=55)\n    public int testHashCode(Object obj) {\n        int h = obj.hashCode();\n        int v = Math.abs(h);\n        return v;\n    }\n\n    @AssertNoWarning(\"AbsoluteValueOfHashCode\")\n    static int falsePositive(Object key) {\n        int rawHash = key.hashCode();\n        return rawHash == Integer.MIN_VALUE ? 0 : Math.abs(rawHash);\n    }\n\n    @AssertNoWarning(\"AbsoluteValueOfHashCode\")\n    static int unaryMinus(Object key) {\n        int rawHash = key.hashCode();\n        return -Math.abs(rawHash);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestMutableServletField.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.IOException;\n\nimport javax.servlet.GenericServlet;\nimport javax.servlet.ServletException;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestMutableServletField {\n\n    @AssertNoWarning(\"*\")\n    public static class TestServletOk extends GenericServlet {\n        private static final long serialVersionUID = 1L;\n        \n        long data;\n        \n        public TestServletOk() {\n            data = System.currentTimeMillis();\n        }\n        \n        public long getData() {\n            return data;\n        }\n\n        @Override\n        public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {\n        }\n    }\n    \n    public static class TestServlet extends GenericServlet {\n        private static final long serialVersionUID = 1L;\n        \n        @AssertWarning(\"MutableServletField\")\n        long data;\n        \n        public long getData() {\n            return data;\n        }\n\n        @Override\n        public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {\n            data = System.currentTimeMillis();\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestNaming.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.IOException;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestNaming {\n    @AssertWarning(\"BadNameOfField\")\n    public String Abc;\n\n    @AssertNoWarning(\"BadNameOfField\")\n    public String abc;\n    \n    @AssertWarning(\"BadNameOfFieldFutureKeyword\")\n    protected String _ = \"\";\n    \n    @AssertNoWarning(\"BadNameOfField\")\n    public final String Empty = \"\";\n    \n    @AssertNoWarning(\"BadNameOfMethod\")\n    public void AB() {\n        System.out.println();\n    }\n\n    @AssertWarning(value=\"BadNameOfMethod\", minScore=28)\n    public void Test() {\n        System.out.println();\n    }\n    \n    @AssertWarning(value=\"BadNameOfMethodSameAsConstructor\", minScore=60)\n    public void TestNaming() {\n        System.out.println();\n    }\n\n    @AssertWarning(value=\"BadNameOfMethodSameAsConstructor\", maxScore=50)\n    public void TestNaming(String s) {\n        throw new UnsupportedOperationException(s);\n    }\n    \n    @AssertWarning(value=\"BadNameOfMethod\", minScore=25, maxScore=28)\n    protected void Test2() {\n        System.out.println();\n    }\n    \n    @AssertWarning(value=\"BadNameOfMethod\", minScore=22, maxScore=25)\n    void Test3() {\n        System.out.println();\n    }\n    \n    @AssertWarning(value=\"BadNameOfMethodMistake\", minScore=55)\n    public int hashcode() {\n        return 31;\n    }\n    \n    @AssertNoWarning(\"BadNameOfMethodMistake\")\n    public int hashcode(int x) {\n        return x;\n    }\n\n    @AssertWarning(value=\"BadNameOfMethodMistake\", minScore=55)\n    public String tostring() {\n        return \"MyClass\";\n    }\n    \n    @AssertWarning(value=\"BadNameOfMethodMistake\", minScore=55)\n    public boolean equal(Object obj) {\n        return obj == this;\n    }\n    \n    public static class Class1 {\n        @AssertWarning(value=\"BadNameOfMethod\", minScore=15, maxScore=20)\n        private void Test4() {\n            System.out.println();\n        }\n\n        @AssertNoWarning(\"BadNameOfMethodMistake\")\n        private int hashcode() {\n            return 31;\n        }\n    }\n    \n    static class Class2 {\n        @AssertWarning(value=\"BadNameOfMethod\", minScore=5, maxScore=10)\n        private void Test5() {\n            System.out.println();\n        }\n\n        @AssertNoWarning(\"BadNameOfMethodMistake\")\n        public static int hashcode() {\n            return 31;\n        }\n    }\n    \n    @AssertWarning(value=\"BadNameOfClass\", minScore=30)\n    public static class cls1 {\n        // empty\n    }\n    \n    @AssertWarning(value=\"BadNameOfClass\", maxScore=20)\n    protected static class cls2 {\n        // empty\n    }\n    \n    @AssertWarning(value=\"BadNameOfClassException\", minScore=40)\n    public static class MyException {\n        // empty\n    }\n    \n    @AssertNoWarning(\"BadNameOfClassException\")\n    public static class MyOkException extends RuntimeException {\n        private static final long serialVersionUID = 1L;\n    }\n\n    @AssertWarning(\"BadNameOfClassSameAsSuperclass\")\n    public static class File extends java.io.File {\n        private static final long serialVersionUID = 1L;\n\n        public File(String pathname) {\n            super(pathname);\n        }\n    }\n    \n    @AssertWarning(\"BadNameOfClassSameAsInterface\")\n    public static class Closeable implements Cloneable, java.io.Closeable {\n        @Override\n        public void close() throws IOException {\n            // empty\n        }\n    }\n    \n    @AssertWarning(\"BadNameOfMethodFutureKeyword\")\n    public void _() {\n        \n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestNegativeRemainder.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.List;\nimport java.util.Random;\nimport java.util.SplittableRandom;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestNegativeRemainder {\n    Random r = new Random(1);\n    SplittableRandom sr = new SplittableRandom();\n    \n    @AssertWarning(\"HashCodeRemainder\")\n    public static Object getHashBucket(Object a[], Object x) {\n        return a[x.hashCode() % a.length];\n    }\n\n    @AssertWarning(\"HashCodeRemainder\")\n    public static Object getHashBucket2(Object a[], Object x) {\n        int i = x.hashCode() % a.length;\n        return a[i];\n    }\n    \n    @AssertWarning(\"HashCodeRemainder\")\n    public static void setHashBucket(Object a[], Object x) {\n        int i = x.hashCode() % a.length;\n        a[i] = 1;\n    }\n    \n    @AssertNoWarning(\"HashCodeRemainder\")\n    public static void setHashBucketOk(Object a[], Object x) {\n        int i = x.hashCode() % a.length;\n        a[1] = i;\n    }\n    \n    @AssertWarning(\"HashCodeRemainder\")\n    public static String getHashBucketList(List<String> a, Object x) {\n        return a.get(x.hashCode() % a.size());\n    }\n    \n    @AssertNoWarning(\"*\")\n    public static void setHashBucketListOk(List<Integer> a, Object x) {\n        a.set(1, x.hashCode() % a.size());\n    }\n    \n    @AssertWarning(\"HashCodeRemainder\")\n    public static void setHashBucketList(List<Integer> a, Object x) {\n        a.set(x.hashCode() % a.size(), 1);\n    }\n    \n    @AssertWarning(\"RandomIntRemainder\")\n    public void setRandomElement(List<Integer> a) {\n        a.set(r.nextInt() % a.size(), 1);\n    }\n    \n    @AssertWarning(\"RandomIntRemainder\")\n    public void setSplittableRandomElement(int[] a) {\n        a[sr.nextInt() % a.length] = 1;\n    }\n    \n    @AssertNoWarning(\"RandomIntRemainder\")\n    public void setElementToRandom(int[] a) {\n        a[1] = sr.nextInt() % a.length;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void setRandomElementOk(List<Integer> a) {\n        a.set(r.nextInt(a.size()), 1);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestNewGetClass.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestNewGetClass {\n    @AssertWarning(\"NewForGetClass\")\n    public void test() {\n        System.out.println(new TestNewGetClass().getClass());\n    }\n\n    @AssertNoWarning(\"NewForGetClass\")\n    public void testOk() {\n        TestNewGetClass x;\n        System.out.println((x = new TestNewGetClass()).getClass());\n        System.out.println(x);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestNoRuntimeRetention.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestNoRuntimeRetention {\n    @Target({ElementType.TYPE, ElementType.METHOD})\n    public @interface MyAnno {} \n\n    @Target({ElementType.TYPE, ElementType.METHOD})\n    @Retention(RetentionPolicy.RUNTIME)\n    public @interface MyAnnoRuntime {} \n    \n    @AssertWarning(\"AnnotationNoRuntimeRetention\")\n    public boolean testNoRuntimeRetention(Object obj) {\n        return obj.getClass().isAnnotationPresent(MyAnno.class);\n    }\n\n    @AssertWarning(\"AnnotationNoRuntimeRetention\")\n    public boolean testNoRuntimeRetention(Method m) {\n        return m.getDeclaredAnnotation(MyAnno.class) != null;\n    }\n\n    @AssertNoWarning(\"*\")\n    public boolean testRuntimeRetention(Method m) {\n        return m.getDeclaredAnnotation(MyAnnoRuntime.class) != null;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestNonShortCircuit.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestNonShortCircuit {\n    boolean f = true;\n    static boolean sf = true;\n    \n    @AssertNoWarning(\"NonShortCircuit*\")\n    public int testNormal(int a, int b) {\n        if(a > 0 && b > 0)\n            return 1;\n        if((a & b) > 0)\n            return 2;\n        if(a > 0 || b > 0)\n            return 3;\n        if((a | b) > 0)\n            return 4;\n        boolean x = a > 0;\n        x |= b > 0;\n        if(x |= b > 0) {\n            return 1;\n        }\n        boolean[] bb = {true, false};\n        bb[0] |= x;\n        f |= x;\n        sf |= x;\n        a = (f ? 1 : 0) | (b << 1) | (b << 2);\n        return 5+(x?2:3);\n    }\n    \n    @AssertWarning(value=\"NonShortCircuit\", minScore=40, maxScore=60) \n    public int testAnd(int a, int b) {\n        if(a > 0 & b > 0)\n            return 1;\n        return 2;\n    }\n    \n    @AssertWarning(value=\"NonShortCircuit\", minScore=40, maxScore=60) \n    public int testOr(int a, int b) {\n        if(a > 0 | b > 0)\n            return 1;\n        return 2;\n    }\n    \n    @AssertNoWarning(\"NonShortCircuit\") \n    @AssertWarning(value=\"NonShortCircuitDangerous\", minScore=40, maxScore=60) \n    public int testOrBoxing(Integer a, Integer b) {\n        if(a > 0 | ++b > 0)\n            return 1;\n        return 2;\n    }\n    \n    @AssertWarning(value=\"NonShortCircuit\", minScore=40, maxScore=60) \n    public int testOrBoxing2(Integer a, Integer b) {\n        if(++b > 0 | a > 0)\n            return 1;\n        return 2;\n    }\n    \n    @AssertWarning(value=\"NonShortCircuitDangerous\", minScore = 60, maxScore = 70)\n    @AssertNoWarning(\"NonShortCircuit\") \n    public int testMethod(Integer a, Integer b) {\n        if(testOrBoxing(a, a) > 0 | ++b > 0)\n            return 1;\n        return 2;\n    }\n    \n    @AssertWarning(value=\"NonShortCircuitDangerous\", minScore=75) \n    @AssertNoWarning(\"NonShortCircuit\") \n    public int testNull(String s) {\n        if(s != null & s.length() > 2)\n            return 1;\n        return 2;\n    }\n    \n    @AssertWarning(value=\"NonShortCircuitDangerous\", minScore=75) \n    @AssertNoWarning(\"NonShortCircuit\") \n    public int testNullOr(String s) {\n        if(s == null | s.length() > 2)\n            return 1;\n        return 2;\n    }\n    \n    @AssertWarning(value=\"NonShortCircuitDangerous\", minScore=75) \n    @AssertNoWarning(\"NonShortCircuit\") \n    public int testInstanceOf(Object s) {\n        if(s instanceof String & ((String)s).length() > 2)\n            return 1;\n        return 2;\n    }\n    \n    @AssertWarning(\"NonShortCircuit\") \n    @AssertNoWarning(\"NonShortCircuitDangerous\") \n    public int testInstanceOf(Object s, boolean b) {\n        if(s instanceof String & b)\n            return 1;\n        return 2;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestNullCheck.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author shustkost\n *\n */\npublic class TestNullCheck {\n    @SuppressWarnings(\"null\")\n    @AssertWarning(\"NullDereferenceGuaranteed\")\n    public void testNullDeref() {\n        String x = null;\n        if (x.isEmpty()) {\n            System.out.println(\"Oops\");\n        }\n    }\n    \n    private void error(String s) {\n        throw new IllegalArgumentException(s);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testErrorCall(String s) {\n        if(s == null) {\n            error(\"null\");\n        }\n        System.out.println(s.trim());\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testLoop(List<String> list) {\n        String s = null;\n        for(String item : list) {\n            s = item;\n        }\n        if(!list.isEmpty()) {\n            System.out.println(s.trim());\n        }\n    }\n    @AssertNoWarning(\"*\")\n    public void testDoubleExpressionNull(int... order) {\n        int length = (order != null) ? order.length : 0;\n        if(length == 1 && order[0] == 1) {\n            length = 0;\n        }\n        System.out.println(length);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testTernaryNull(X x) {\n        X parent = x == null ? null : x.parent;\n        if(parent == null) {\n            throw new IllegalArgumentException();\n        }\n        System.out.println(parent.toString());\n    }\n\n    @AssertWarning(\"NullDereferenceExceptional\")\n    public void testNullExceptional(String s) {\n        Integer x = null;\n        try {\n            x = Integer.valueOf(s);\n        } catch (NumberFormatException ex) {\n            ex.printStackTrace();\n        }\n        if (x > 0) {\n            System.out.println(\"Bigger\");\n        }\n    }\n\n    @AssertWarning(\"RedundantComparisonNull\")\n    public void testRedundantNull(String s) {\n        String a = null;\n        if (s == null) {\n            System.out.println(1);\n            if (s == a) {\n                System.out.println(2);\n            }\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testNullThrow(String s) {\n        boolean invalid = s == null || s.isEmpty();\n        if(invalid) {\n            throw new IllegalArgumentException();\n        }\n        System.out.println(s.trim());\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testFileOpen(File f1, File f2, File f3) throws IOException {\n        InputStream is = null;\n        boolean success = false;\n        try {\n            is = new FileInputStream(f1);\n            success = true;\n        }\n        catch(IOException e) {\n        }\n        if(!success) {\n            try {\n                is = new FileInputStream(f2);\n                success = true;\n            } catch (IOException e) {\n            }\n        }\n        if(!success) {\n            try {\n                is = new FileInputStream(f3);\n            } catch (IOException e) {\n                throw new IllegalStateException(e);\n            }\n        }\n        System.out.println(is.read());\n    }\n    \n    @AssertWarning(value = \"RedundantComparisonNullNonNull\", maxScore = 45)\n    public void testRedundantNotNull() {\n        TestNullCheck other = null;\n        if (this != other) {\n            System.out.println(\"Always\");\n        }\n    }\n    \n    class X {\n        X parent;\n    }\n    \n    @AssertWarning(\"RedundantNullCheckNull\")\n    public void testRedundantNullWhile(X x) {\n        while(x != null) {\n            x = x.parent;\n        }\n        if(x == null) {\n            System.out.println(\"Always\");\n        }\n    }\n\n    @AssertWarning(value = \"RedundantEqualsNullCheck\", minScore = 55)\n    public void testRedundantEqualsNull() {\n        TestNullCheck other = null;\n        if (this.equals(other)) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public String safeTrim(String s) {\n        return s == null ? null : s.trim();\n    }\n\n    @AssertNoWarning(\"*\")\n    public void addItem(Map<Integer, List<String>> map, int from, int to, List<String> values) {\n        List<String> list = null;\n        System.out.println(list);\n        try {\n            for (int key = from; key < to; key++) {\n                list = map.get(key);\n                if (list == null) {\n                    list = new ArrayList<>(values);\n                } else {\n                    list.addAll(values);\n                }\n                map.put(key, list);\n            }\n        } catch (Exception ex) {\n            list = null;\n        }\n        System.out.println(list);\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testOk(String s, boolean b) {\n        if (b) {\n            System.out.println(\"Hello\");\n        } else if (s != null) {\n            System.out.println(s);\n        } else {\n            throw new IllegalArgumentException();\n        }\n        System.out.println(s.trim());\n    }\n\n    @AssertWarning(value = \"RedundantNullCheckNull\", maxScore = 50)\n    public void testNull(String s) {\n        if (s == null) {\n            System.out.println(\"Hello\");\n            if (null == s) {\n                System.out.println(\"Yes\");\n            }\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testAssert(int i) {\n        String s;\n        if (i > 0) {\n            s = String.valueOf(i);\n        } else {\n            s = null;\n        }\n        if (i > 0) {\n            assert s != null;\n            System.out.println(s.trim());\n        }\n    }\n\n    @AssertWarning(\"NullDereferencePossible\")\n    public void testNullDerefPossible(boolean b) {\n        String x = null;\n        if (b) {\n            x = \"test\";\n        }\n        if (x.isEmpty()) {\n            System.out.println(\"Oops\");\n        }\n    }\n\n    @SuppressWarnings(\"unused\")\n    @AssertWarning(\"RedundantNullCheckDeref\")\n    public void testRedundantDeref(String str) {\n        System.out.println(str.trim());\n        if (str == null) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @SuppressWarnings(\"unused\")\n    @AssertWarning(\"RedundantNullCheck\")\n    public void testRedundant() {\n        String str = \"const\";\n        if (str == null) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @SuppressWarnings(\"unused\")\n    @AssertWarning(\"RedundantNullCheckChecked\")\n    public void testRedundantDoubleCheck(String s) {\n        if (s == null) {\n            throw new IllegalArgumentException();\n        }\n        if (s == null) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @SuppressWarnings(\"unused\")\n    @AssertWarning(\"RedundantNullCheckChecked\")\n    public void testRedundantDoubleCheckCall(String s) {\n        Objects.requireNonNull(s);\n        if (s == null) {\n            System.out.println(\"Never\");\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testWithBooleanFlag(String s) {\n        boolean f = s == null || s.isEmpty();\n        if(f) {\n            s = \"none\";\n        }\n        System.out.println(s.trim());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestNumberConstructor.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestNumberConstructor {\n    @AssertWarning(value=\"NumberConstructor\", minScore = 42)\n    public Integer testInteger() {\n        return new Integer(123);\n    }\n\n    @AssertWarning(value=\"NumberConstructor\", minScore = 20, maxScore = 35)\n    public Integer testInteger2() {\n        return new Integer(130);\n    }\n\n    @AssertWarning(value=\"NumberConstructor\", maxScore = 15)\n    public Long testLong() {\n        return new Long(130);\n    }\n\n    @AssertWarning(value=\"NumberConstructor\", maxScore = 42)\n    public Integer testInteger3(int x) {\n        return new Integer(x);\n    }\n\n    @AssertWarning(value=\"NumberConstructor\", maxScore = 42)\n    public Character testChar(char x) {\n        return new Character(x);\n    }\n\n    @AssertWarning(value=\"BooleanConstructor\", minScore = 42, maxScore=55)\n    public Boolean testBoolean(boolean x) {\n        return new Boolean(x);\n    }\n\n    @AssertNoWarning(\"NumberConstructor\")\n    public Float testFloat(float x) {\n        return new Float(x);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestNumericComparison.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestNumericComparison {\n    @AssertNoWarning(\"*\")\n    public void testByte(byte b) {\n        if (b + 1 > Byte.MAX_VALUE) {\n            System.out.println(\"possible?\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testChar(char c) {\n        if (c < 0) {\n            System.out.println(\"Never!\");\n        }\n        if (c >= 0) {\n            System.out.println(\"Always\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testShort(short s) {\n        if (s == 0xFEF0) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(value = \"ComparisonWithOutOfRangeValue\", minScore = 75)\n    public void testInt(boolean b) {\n        int x = b ? 1 : 2;\n        if (x > 2) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(value = \"ComparisonWithOutOfRangeValue\", maxScore = 60)\n    public void testIntAlways(boolean b) {\n        int x = b ? 1 : 2;\n        if (x < 3) {\n            System.out.println(\"Always!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testIntPhi(boolean b) {\n        int x = 1;\n        if (b)\n            x = 2;\n        if (x > 2) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertNoWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testCharOk(char c) {\n        int r = c - 'a';\n        if (r < 0) {\n            System.out.println(\"Ok!\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testAssert(char c) {\n        assert c >= 0;\n        if (c == 'x') {\n            System.out.println(\"X!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testByte(Byte b) {\n        int i = b.byteValue();\n        if (i < 0x80) {\n            System.out.println(\"Always\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testInt(int c) {\n        if (c < 0x100000000000L) {\n            System.out.println(\"Always!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testArrayLength(int[] array) {\n        if (array.length < 0) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"SwitchBranchUnreachable\")\n    public int testArrayLengthSwitch(int[] array) {\n        switch (array.length) {\n        case 0:\n            return -1;\n        case 1:\n            return 0;\n        case 2:\n            return 10;\n        case Integer.MAX_VALUE:\n            return 12;\n        case -1:\n            return Integer.MIN_VALUE;\n        default:\n            return -2;\n        }\n    }\n\n    @AssertNoWarning(\"SwitchBranchUnreachable\")\n    public int testArrayLengthSwitchOk(int[] array) {\n        switch (array.length) {\n        case 0:\n            return -1;\n        case 1:\n            return 0;\n        case 2:\n            return 10;\n        case Integer.MAX_VALUE:\n            return 12;\n        default:\n            return -2;\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testListLength(ArrayList<String> list) {\n        if (list.size() == -1) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertNoWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testBitOpOk(int input) {\n        int result = input & 0x1FFF0;\n        if (result > 0xFFFF) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testBitOp(int input) {\n        int result = 0xFF0 & input;\n        if (result > 0xFFFF) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testBitOpSelf(int input) {\n        input &= 0xFF0;\n        if (input > 0xFFFF) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testBitOpPhi(int input, boolean b) {\n        int mask = b ? 0xFF : 0x1F0;\n        int result = mask & input;\n        if (result > 0x1FF) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testBitOpPhi2(int input, boolean b) {\n        int mask = b ? 0xFF : 0x1F0;\n        int result = mask & input;\n        if (result < 0) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertNoWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testBitOpPhiOk(int input, boolean b) {\n        int mask = b ? 0xF0 : 0x1FF;\n        int result = mask & input;\n        if (result >= 0x1FF) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertNoWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testBitOpPhiOk2(int input, boolean b) {\n        int mask = b ? 0xF0 : -1;\n        int result = mask & input;\n        if (result == Integer.MIN_VALUE) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testRem(int input) {\n        int result = input % 3;\n        if (result == 3) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testRem(List<String> list) {\n        int result = list.size() % 3;\n        if (result < 0) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testRemPhi(List<String> list, boolean b) {\n        int x = b ? 3 : 5;\n        int result = list.size() % x;\n        if (result > 5) {\n            System.out.println(\"Never!\");\n        }\n    }\n\n    @AssertNoWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testShrOk(int input) {\n        int result = input >> 10;\n        if (result == 0x1FFFFF || result == -0x200000) {\n            System.out.println(\"Ok\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testShr(int input) {\n        int result = input >> 10;\n        if (result == 0x200000) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @AssertNoWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testUShrOk(int input) {\n        int result = input >>> 10;\n        if (result == 0x3FFFFF) {\n            System.out.println(\"Ok\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void testUShr(int input) {\n        int result = input >>> 10;\n        if (result == 0x400000) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @AssertWarning(\"CheckForOddnessFailsForNegative\")\n    public void testRem2(int input) {\n        if (input % 2 == 1) {\n            System.out.println(\"odd\");\n        }\n    }\n\n    @AssertNoWarning(\"CheckForOddnessFailsForNegative\")\n    public void testRem2Ok(List<String> list) {\n        if (list.size() % 2 == 1) {\n            System.out.println(\"odd\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public int countMembers(int[] bits) {\n        int count = 0;\n        for (int i = 0; i < bits.length; i++) {\n            int x = bits[i];\n            while (x != 0) {\n                count++;\n                x &= (x - 1);\n            }\n        }\n        return count;\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void checkRandom(Random r) {\n        if (r.nextInt(10) > 15) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @AssertWarning(\"ComparisonWithOutOfRangeValue\")\n    public void checkWithCatch(String s) {\n        int x;\n        try {\n            x = Byte.parseByte(s);\n        } catch (NumberFormatException ex) {\n            x = 130;\n        }\n        if (x > 140) {\n            System.out.println(\"Never!\");\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestNumericPromotion.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestNumericPromotion {\n    @AssertWarning(value=\"IntegerMultiplicationPromotedToLong\", minScore = 65)\n    public long testMultiplication(int num) {\n        return num * 365 * 86400 * 1000;\n    }\n\n    @AssertWarning(value=\"IntegerMultiplicationPromotedToLong\", minScore = 55, maxScore = 60)\n    public long testMultiplication2(int num) {\n        return num * 365 * 86400;\n    }\n\n    @AssertWarning(value=\"IntegerMultiplicationPromotedToLong\", minScore = 45, maxScore = 50)\n    public long testMultiplication3(int num) {\n        return num * 86400;\n    }\n\n    @AssertWarning(value=\"IntegerMultiplicationPromotedToLong\", minScore = 35, maxScore = 40)\n    public long testMultiplication4(int num) {\n        return num * 365;\n    }\n\n    @AssertWarning(value=\"IntegerMultiplicationPromotedToLong\", minScore = 25, maxScore = 30)\n    public long testMultiplication5(int num) {\n        return num * 60;\n    }\n\n    @AssertWarning(value=\"IntegerMultiplicationPromotedToLong\", minScore = 15, maxScore = 20)\n    public long testMultiplication6(int num) {\n        return num * 2;\n    }\n\n    @AssertNoWarning(\"IntegerMultiplicationPromotedToLong\")\n    public long testMultiplicationTwoNum(int num, int num2) {\n        return num * num2;\n    }\n\n    @AssertWarning(\"IntegerDivisionPromotedToFloat\")\n    public double divide(int x, int y) {\n        return x / y;\n    }\n\n    @AssertNoWarning(\"IntegerDivisionPromotedToFloat\")\n    public double percent(int val, int total) {\n        return val * 100 * 10 / total / 10.0;\n    }\n    \n    @AssertWarning(value=\"IntegerDivisionPromotedToFloat\", maxScore = 50, minScore = 40)\n    public double byTen(int val) {\n        return val / 10;\n    }\n    \n    @AssertWarning(value=\"IntegerDivisionPromotedToFloat\", maxScore = 45, minScore = 35)\n    public double pow(double x, int val) {\n        return Math.pow(x, val/2);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public String format(int length) {\n        return String.valueOf((length / 100) / 10.0);\n    }\n    \n    @AssertNoWarning(\"IntegerDivisionPromotedToFloat\")\n    public double check(int val) {\n        int res = val / 3;\n        System.out.println(res);\n        return res * 3.0;\n    }\n    \n    @AssertWarning(\"IntegerDivisionPromotedToFloat\")\n    public double divideByTwo(double x, double y) {\n        double res = (int)(x - y)/2;\n        return res;\n    }\n\n    @AssertWarning(\"IntegerPromotionInCeilOrRound\")\n    public int divideAndRound(int x, int y) {\n        return Math.round(x / y);\n    }\n    \n    @AssertWarning(\"IntegerPromotionInCeilOrRound\")\n    public long divideAndCeil(long x, long y) {\n        return (long) Math.ceil(x / y);\n    }\n    \n    @AssertWarning(\"IntegerDivisionPromotedToFloat\")\n    public float divideFloat(int x, long y) {\n        return x / y;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public float divideFloatOk(int x, int y) {\n        return (float)x / y;\n    }\n    \n    @AssertWarning(\"IntegerDivisionPromotedToFloat\")\n    public void divideWithNeg(int x) {\n        int res = x/2;\n        System.out.println((double)res);\n        System.out.println((double)(-res));\n    }\n\n    @AssertNoWarning(\"*\")\n    public void divideWithNegOk(int x) {\n        int res = x/2;\n        System.out.println((double)res);\n        System.out.println(-res);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestRandomUsage.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.security.SecureRandom;\nimport java.util.Random;\nimport java.util.SplittableRandom;\nimport java.util.concurrent.ThreadLocalRandom;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestRandomUsage {\n    @AssertWarning(\"RandomDoubleToInt\")\n    public int test() {\n        return (int) Math.random();\n    }\n\n    @AssertWarning(value=\"RandomNextIntViaNextDouble\", maxScore=35)\n    public int testMul() {\n        return (int) (10*Math.random());\n    }\n    \n    @AssertWarning(\"RandomDoubleToInt\")\n    public int testRnd(Random r) {\n        return (int) r.nextDouble();\n    }\n    \n    @AssertWarning(\"RandomNextIntViaNextDouble\")\n    public int testRndMult(Random r) {\n        return (int) (r.nextDouble()*10);\n    }\n    \n    @AssertWarning(\"RandomDoubleToInt\")\n    public int testRnd() {\n        return (int) ThreadLocalRandom.current().nextDouble();\n    }\n    \n    @AssertNoWarning(\"Random*\")\n    public int testRndOk() {\n        return (int) ThreadLocalRandom.current().nextDouble(100);\n    }\n    \n    @AssertWarning(\"RandomUsedOnlyOnce\")\n    public int testRndOnce() {\n        return new SplittableRandom().nextInt(10, 20);\n    }\n    \n    @AssertNoWarning(\"RandomUsedOnlyOnce\")\n    public int[] testRndArr() {\n        return new Random().ints(100).toArray();\n    }\n    \n    @AssertNoWarning(\"RandomUsedOnlyOnce\")\n    public int testRndOnceSecure() {\n        return new SecureRandom().nextInt();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestRedundantInterfaces.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.LinkedList;\nimport java.util.RandomAccess;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\n@AssertWarning(\"RedundantInterface\")\npublic class TestRedundantInterfaces extends TreeSet<String> implements Set<String>, Collection<String> {\n    private static final long serialVersionUID = 1L;\n\n    @AssertNoWarning(\"RedundantInterface\")\n    public static class NoRedundantInterfaces extends LinkedList<String> implements RandomAccess, Serializable {\n        private static final long serialVersionUID = 1L;\n    }\n    \n    @AssertNoWarning(\"RedundantInterface\")\n    public static class NoRedundantInterfaces2 extends NoRedundantInterfaces implements Serializable {\n        private static final long serialVersionUID = 1L;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestRedundantStreamCalls.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestRedundantStreamCalls {\n    @AssertWarning(\"RedundantStreamForEach\")\n    public void testSet(Set<String> x) {\n        x.stream().forEach(System.out::println);\n    }\n\n    @AssertWarning(\"RedundantStreamForEach\")\n    public void testArrayList(ArrayList<String> x) {\n        x.stream().forEachOrdered(System.out::println);\n    }\n    \n    @AssertWarning(\"RedundantStreamFind\")\n    public boolean testArrayListFind(ArrayList<String> x) {\n        return x.stream().filter(str -> str.startsWith(\"xyz\")).findFirst().isPresent();\n    }\n    \n    @AssertWarning(\"RedundantStreamFind\")\n    public boolean testStreamFind(Set<Integer> x) {\n        Stream<Integer> set = x.parallelStream();\n        return set.filter(i -> i > 0).findAny().isPresent();\n    }\n    \n    @AssertWarning(\"RedundantStreamFind\")\n    public boolean testStreamFindVar(Set<Integer> x) {\n        Optional<Integer> opt = x.parallelStream().filter(i -> i > 0).findAny();\n        return opt.isPresent();\n    }\n    \n    @AssertNoWarning(\"*\")\n    public Integer testStreamFindOk(Set<Integer> x) {\n        Optional<Integer> opt = x.parallelStream().filter(i -> i > 0).findAny();\n        if(opt.isPresent()) {\n            return opt.get();\n        }\n        return null;\n    }\n    \n    @AssertWarning(\"RedundantStreamFind\")\n    public boolean testIntStreamFind(IntStream is) {\n        return is.filter(i -> i > 0).findAny().isPresent();\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testIntermediate(ArrayList<String> x) {\n        x.stream().map(String::trim).forEachOrdered(System.out::println);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testParallel(ArrayList<String> x) {\n        x.parallelStream().forEach(System.out::println);\n    }\n    \n    @AssertWarning(\"RedundantCollectionStream\")\n    public Stream<String> testRedundantEmpty() {\n        return Collections.<String>emptyList().stream();\n    }\n    \n    @AssertWarning(\"RedundantCollectionStream\")\n    public Stream<String> testRedundantSingleton() {\n        return Collections.singleton(\"a\").stream();\n    }\n    \n    @AssertWarning(\"RedundantCollectionStream\")\n    public Stream<String> testRedundantArray() {\n        return Arrays.asList(\"a\", \"b\", \"c\").stream();\n    }\n    \n    @AssertWarning(\"StreamCountFromCollection\")\n    public long testStreamCount(List<Integer> collection) {\n        return collection.stream().count();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestRegexProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.File;\nimport java.nio.file.FileSystems;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestRegexProblems {\n    @AssertWarning(\"RegexBadSyntax\")\n    public void testBadRegex(String test) {\n        if(test.matches(\"***\")) {\n            System.out.println(\"Matches\");\n        }\n    }\n\n    @AssertWarning(\"RegexUnintended\")\n    public void testPipe(String test) {\n        for(String part : test.split(\"|\")) {\n            System.out.println(part);\n        }\n    }\n    \n    @AssertWarning(\"RegexUnintended\")\n    public void testDot(String test) {\n        for(String part : test.split(\".\")) {\n            System.out.println(part);\n        }\n    }\n\n    @AssertNoWarning(\"RegexUnintended\")\n    public String testDotReplace(String test) {\n        return test.replaceAll(\".\", \" \");\n    }\n\n    @AssertWarning(\"RegexUnintended\")\n    public String testDotReplaceFirst(String test) {\n        return test.replaceFirst(\".\", \" \");\n    }\n\n    @AssertWarning(\"RegexFileSeparator\")\n    public String[] testFileSeparator(String test) {\n        return test.split(File.separator);\n    }\n    \n    @AssertWarning(\"RegexFileSeparator\")\n    public String[] testFileSeparator2(String test) {\n        return test.split(FileSystems.getDefault().getSeparator());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestReturnNull.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestReturnNull {\n    @AssertWarning(\"OptionalReturnNull\")\n    Optional<String> testOptional(int a) {\n        Optional<String> x = null;\n        if(a > 5)\n            return Optional.of(String.valueOf(a));\n        return x;\n    }\n\n    @AssertWarning(\"BooleanReturnNull\")\n    Boolean testBoolean(int a) {\n        if(a > 5)\n            return true;\n        return null;\n    }\n    \n    @AssertWarning(value=\"ArrayReturnNull\", minScore = 35)\n    public int[] testArray(int a) {\n        if(a > 5)\n            return new int[] {a};\n        return null;\n    }\n    \n    @AssertWarning(value=\"ArrayReturnNull\", maxScore = 30)\n    public int[] testArrayNull() {\n        return null;\n    }\n    \n    @AssertWarning(\"OptionalReturnNull\")\n    String testInLambda(String x) {\n        Function<String, Optional<String>> s = y -> null;\n        return s.apply(x).get();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestRoughConstants.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestRoughConstants {\n    private static final float PI_FLOAT = 3.14159f;\n    private static final double PI_DOUBLE = 3.14159;\n\n    @AssertNoWarning(\"*\")\n    public void testNan() {\n        double a;\n        a = Double.NaN;\n        System.out.println(a);\n        a = Double.POSITIVE_INFINITY;\n        System.out.println(a);\n        a = Double.NEGATIVE_INFINITY;\n        System.out.println(a);\n        float b;\n        b = Float.NaN;\n        System.out.println(b);\n        b = Float.POSITIVE_INFINITY;\n        System.out.println(b);\n        b = Float.NEGATIVE_INFINITY;\n        System.out.println(b);\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testNoWarning() {\n        double a;\n        a = Math.PI; // Exact\n        System.out.println(a);\n        a = 3.141512345; // Explicitly specified something different from PI\n        System.out.println(a);\n        a = 3.1417;\n        System.out.println(a);\n        a = 3.1414;\n        System.out.println(a);\n        float b;\n        b = (float)Math.PI; // Exact value converted to float\n        System.out.println(b);\n        b = 3.141512345f; // Explicitly specified something different from PI\n        System.out.println(b);\n        b = 3.1417f;\n        System.out.println(b);\n        b = 3.1414f;\n        System.out.println(b);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=50)\n    public void testPi1() {\n        System.out.println(3.141592);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=30)\n    public void testPi2() {\n        System.out.println(3.141592612345); // Something different, but too close to PI, thus suspicious\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=40)\n    public void testPi3() {\n        System.out.println(3.1415f);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=50)\n    public void testPi4() {\n        System.out.println(PI_DOUBLE);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=50)\n    public void testPi5() {\n        System.out.println(PI_FLOAT);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=50)\n    public void testPi6() {\n        System.out.println(PI_FLOAT);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=40)\n    public void test2Pi1() {\n        System.out.println(2*3.141592);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=30)\n    public void test2Pi2() {\n        System.out.println(2*3.1416);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=30)\n    public void test2Pi3() {\n        System.out.println(2*3.1415);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=30)\n    public void test2Pi4() {\n        System.out.println(6.2831);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=30)\n    public void testE1() {\n        System.out.println(2.7183);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=30)\n    public void testE2() {\n        System.out.println(2.71828f);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", maxScore=20)\n    public void testE2Digits() {\n        // Too far away from real value and E is not very popular number\n        System.out.println(2.72);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", minScore=30)\n    public void testPi2Digits() {\n        // Pi is more popular, thus likely a bug\n        System.out.println(3.14);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", maxScore=30)\n    public void testDoubleArray() {\n        double[] arr = new double[] {2.7183, 2.7184, 2.7185};\n        System.out.println(arr[0]);\n    }\n\n    @AssertWarning(value=\"RoughConstantValue\", maxScore=30)\n    public void testDoubleArray2() {\n        Double[] arr = new Double[] {2.7183, 2.7184, 2.7185};\n        System.out.println(arr[0]);\n    }\n\n    @AssertWarning(\"RoughConstantValue\")\n    public void testDoubleArrayHighPrecision() {\n        Double[] arr = new Double[] {2.718281828, 2.719, 2.72};\n        System.out.println(arr[0]);\n    }\n\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSameBranches.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.stream.Stream;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestSameBranches {\n    @AssertWarning(\"SameBranchesIf\")\n    public void testSame(int a) {\n        if (a > 0) {\n            System.out.println(\"Foo\");\n        } else {\n            System.out.println(\"Foo\");\n        }\n    }\n\n    @AssertWarning(\"SameBranchesTernary\")\n    public int testSame(int a, int[] b) {\n        return a > 0 ? b[a++] : b[a++];\n    }\n\n    @AssertWarning(\"SameBranchesIf\")\n    public void testSameComplex(int a, int b) {\n        if (a > 0) {\n            if (b > 0) {\n                try {\n                    while (b < 10) {\n                        System.out.println(\"Foo\");\n                        b += Stream.of(1, 2, 3).reduce((m, n) -> n + m).get();\n                    }\n                } catch (NullPointerException | IllegalAccessError e) {\n                    System.out.println(\"oops...\" + e);\n                }\n            }\n        } else {\n            if (b > 0) {\n                try {\n                    while (b < 10) {\n                        System.out.println(\"Foo\");\n                        b += Stream.of(1, 2, 3).reduce((x, y) -> x + y).get();\n                    }\n                } catch (NullPointerException | IllegalAccessError e) {\n                    System.out.println(\"oops...\" + e);\n                }\n            }\n        }\n    }\n\n    @AssertNoWarning(\"SameBranchesIf\")\n    public void testSameDiffLambda(int a, int b) {\n        if (a > 0) {\n            System.out.println(Stream.of(1, 2, 3).reduce((m, n) -> n - m));\n        } else {\n            System.out.println(Stream.of(1, 2, 3).reduce((m, n) -> m - n));\n        }\n    }\n\n    @AssertWarning(\"SameBranchesIf\")\n    public void testSameLambdaCapture(int a, int b) {\n        if (a > 0) {\n            System.out.println(Stream.of(1, 2, 3).map(x -> x + a).reduce((m, n) -> n + m));\n        } else {\n            System.out.println(Stream.of(1, 2, 3).map(x -> x + a).reduce((m, n) -> m + n));\n        }\n    }\n    \n    @AssertWarning(\"SameBranchesIf\")\n    public void testSameSwitch(int a, int b) {\n        if (a > 0) {\n            switch(b) {\n            case 1:\n                System.out.println(\"One!\");\n                break;\n            case 2:\n                System.out.println(\"Two!\");\n            }\n        } else {\n            switch(b) {\n            case 1:\n                System.out.println(\"One!\");\n                break;\n            case 2:\n                System.out.println(\"Two!\");\n            }\n        }\n    }\n    \n    @AssertNoWarning(\"SameBranchesIf\")\n    public void testDiffComplex(int a, int b) {\n        if (a > 0) {\n            if (b > 0) {\n                try {\n                    while (b < 10) {\n                        System.out.println(\"Foo\");\n                        b++;\n                    }\n                } catch (NullPointerException | IllegalAccessError e) {\n                    System.out.println(\"oops...\" + e);\n                }\n            }\n        } else {\n            if (b > 0) {\n                try {\n                    while (b < 10) {\n                        System.out.println(\"Foo\");\n                        b++;\n                    }\n                } catch (NullPointerException | IllegalAccessError e) {\n                    System.out.println(\"oopss...\" + e);\n                }\n            }\n        }\n    }\n\n    @AssertNoWarning(\"SameBranchesIf\")\n    public void testDiff(int a) {\n        if (a > 0) {\n            System.out.println(\"Foo\");\n        } else {\n            System.out.println(\"Bar\");\n        }\n    }\n    \n    @AssertWarning(\"SameBranchesSwitch\")\n    public void testSwitch(int a) {\n        switch(a) {\n        case 1:\n            System.out.println(\"Foo\");\n            break;\n        case 2:\n            System.out.println(\"Bar\");\n            break;\n        case 3:\n            System.out.println(\"Bar\");\n            break;\n        case 4:\n            System.out.println(\"Bar\");\n            break;\n        case 5:\n        case 6:\n            System.out.println(\"Foo\");\n            break;\n        default:\n            System.out.println(\"Baz\");\n        }\n    }\n    \n    @AssertNoWarning(\"SameBranchesSwitch\")\n    public void testSwitchFallthrough(int a) {\n        switch(a) {\n        case 1:\n            System.out.println(\"Foo\");\n        case 4:\n            System.out.println(\"Foo\");\n        case 5:\n            System.out.println(\"Foo\");\n        default:\n            System.out.println(\"Baz\");\n        }\n    }\n    \n    @AssertNoWarning(\"SameBranchesDefault\")\n    public void testSwitchDefault(int a) {\n        switch(a) {\n        case 1:\n            System.out.println(\"Foo\");\n            break;\n        case 4:\n            System.out.println(\"Bar\");\n            break;\n        default:\n            System.out.println(\"Foo\");\n            break;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSameIfChain.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestSameIfChain {\n    int f = Math.random() > 0.5 ? 3 : 2;\n    \n    @AssertWarning(\"SameConditionChain\")\n    public void testSimple(int x) {\n        if(x > 0) {\n            System.out.println(1);\n        }\n        if(0 < x) {\n            System.out.println(2);\n        }\n    }\n\n    @AssertWarning(\"SameConditionChain\")\n    public void testField(int x) {\n        if(x > f) {\n            System.out.println(1);\n        }\n        if(f < x) {\n            System.out.println(2);\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testOk(int x) {\n        if(x > 0) {\n            x--;\n        }\n        if(x > 0) {\n            System.out.println(2);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSelfAssignment.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestSelfAssignment {\n    Object f;\n    static Object st;\n    int pos;\n    static int staticPos;\n    \n    @AssertWarning(\"SelfAssignmentField\")\n    void test() {\n        Object a = f;\n        f = a;\n    }\n    \n    @AssertWarning(\"SelfAssignmentField\")\n    void testArray() {\n        TestSelfAssignment[] arr = {new TestSelfAssignment()};\n        arr[0].f = arr[0].f;\n    }\n    \n    @AssertWarning(\"SelfAssignmentField\")\n    void testArray(int a) {\n        TestSelfAssignment[] arr = {new TestSelfAssignment()};\n        arr[a+1].f = arr[1+a].f;\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentField\")\n    void testArrayOk() {\n        TestSelfAssignment[] arr = {new TestSelfAssignment(), new TestSelfAssignment()};\n        arr[0].f = arr[1].f;\n    }\n\n    @AssertNoWarning(\"*\")\n    void testArrayOk2(int i, int[] arr) {\n        arr[i] = arr[++i];\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentField\")\n    void testArraySideEffect() {\n        TestSelfAssignment[] arr = {new TestSelfAssignment(), new TestSelfAssignment()};\n        int i=0;\n        arr[i++].f = arr[i++].f;\n    }\n    \n    @AssertWarning(\"SelfAssignmentField\")\n    void testThis() {\n        TestSelfAssignment copy = this;\n        Object a = f;\n        copy.f = a;\n    }\n    \n    @AssertNoWarning(\"*\")\n    void testIncrement(int[] data) {\n        int curPos = pos;\n        if(data[pos++] > 0) {\n            pos = curPos;\n        }\n        System.out.println(\"ok\");\n    }\n    \n    @AssertNoWarning(\"*\")\n    static void testIncrementStatic(int[] data) {\n        int curPos = staticPos;\n        if(data[staticPos++] > 0) {\n            staticPos = curPos;\n        }\n        System.out.println(\"ok\");\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentField\")\n    void testOk() {\n        f = new TestSelfAssignment().f;\n    }\n\n    @AssertWarning(\"SelfAssignmentField\")\n    void testStatic() {\n        Object a = st;\n        st = a;\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentField\")\n    void testSideEffectStatic() {\n        Object a = st;\n        st = null;\n        System.out.println(st);\n        st = a;\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentField\")\n    void testSideEffect() {\n        Object a = f;\n        f = null;\n        System.out.println(f);\n        f = a;\n    }\n\n    @AssertNoWarning(\"SelfAssignmentField\")\n    void testTwoObjects() {\n        TestSelfAssignment t1 = new TestSelfAssignment();\n        TestSelfAssignment t2 = new TestSelfAssignment();\n        t1.f = t2.f;\n    }\n\n    @AssertWarning(\"SelfAssignmentArrayElement\")\n    void testArraySelf(int[] a, int idx) {\n        a[idx] = a[idx];\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentArrayElement\")\n    void testArraySelfDiffArray(int[] a, int[] b, int idx) {\n        a[idx] = b[idx];\n    }\n    \n    @AssertWarning(\"SelfAssignmentArrayElement\")\n    void testArraySelfCopyArray(int[] a, int[] b, int idx) {\n        a = b;\n        a[idx] = b[idx];\n    }\n    \n    @AssertWarning(\"SelfAssignmentArrayElement\")\n    void testArraySelfVar(int[] a, int idx) {\n        int x = a[idx];\n        a[idx] = x;\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentArrayElement\")\n    void testArraySelfChanged(int[] a, int idx) {\n        int x = a[idx];\n        a[idx] = 1;\n        a[idx] = x;\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentArrayElement\")\n    void testArraySelfSideEffect(int[] a, int idx) {\n        int x = a[idx];\n        update(a);\n        a[idx] = x;\n    }\n    \n    private void update(int[] arr) {\n        arr[0] = 2;\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentArrayElement\")\n    void testArraySelfDiffIndex(int[] a, int idx) {\n        a[idx++] = a[idx++];\n    }\n    \n    @AssertNoWarning(\"SelfAssignmentArrayElement\")\n    void testNew() {\n        int[] a = new int[10];\n        int[] b = new int[10];\n        a[0] = b[0];\n    }\n\n    @SuppressWarnings(\"cast\")\n    @AssertWarning(\"SelfAssignmentLocal\")\n    @AssertNoWarning(\"SelfAssignmentLocalInsteadOfField\")\n    void testLocal(int a) {\n        a = (int)a; // cast to prevent error in Eclipse\n    }\n    \n    @SuppressWarnings(\"cast\")\n    @AssertWarning(\"SelfAssignmentLocalInsteadOfField\")\n    @AssertNoWarning(\"SelfAssignmentLocal\")\n    void testLocal(Object f) {\n        f = (Object)f; // cast to prevent error in Eclipse\n    }\n\n    public static class HeapNode {\n        public HeapNode[] myChildren;\n        public String myEdge;\n    }\n\n    @AssertNoWarning(\"*\")\n    void heapify(HeapNode node) {\n        while (true) {\n            HeapNode min = node;\n            for (int i = 0; i < 2; i++) {\n                HeapNode child = node.myChildren[i];\n                if (child != null && child.myEdge.length() < min.myEdge.length()) {\n                    min = child;\n                }\n            }\n            if (min != node) {\n                String t = min.myEdge;\n                min.myEdge = node.myEdge;\n                node.myEdge = t;\n                node = min;\n            } else {\n                break;\n            }\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    void testWithCatch() {\n        Object oldVal = f;\n        try {\n            test();\n        }\n        catch(Exception exc) {\n            f = oldVal;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSelfComputation.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.List;\n\nimport org.junit.Assert;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestSelfComputation {\n    @AssertWarning(\"SelfComputation\")\n    public int test(int a, int b) {\n        return (a - b) / (a - b);\n    }\n\n    @AssertWarning(\"SelfComputation\")\n    public int test(int[] x) {\n        return x[1] - x[1];\n    }\n\n    @AssertWarning(\"SelfComparison\")\n    public boolean testCmp(int[] x) {\n        return x[1] == x[1];\n    }\n    \n    @AssertWarning(\"SelfComparison\")\n    public boolean testCmp(double[] x, double[] y) {    \n        return x[1] + y[0] >= x[1] + y[0];\n    }\n    \n    @AssertWarning(\"SelfEquals\")\n    public boolean testEquals(String s1) {    \n        return s1.equals(s1);\n    }\n    \n    @AssertNoWarning(\"SelfEquals\")\n    public void testMyEquals(Object obj) {\n        Assert.assertTrue(obj.equals(obj));\n    }\n    \n    @AssertNoWarning(\"SelfComputation\")\n    public int test(int[] x, int idx) {\n        return x[idx++] - x[idx++];\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testLambdas(List<Integer> l1, List<Integer> l2) {\n        l1.forEach(a -> {\n            l2.forEach(b -> {\n                System.out.println(a - b);\n            });\n        });\n    }\n    \n\n    // Fails due to Procyon bug, reported https://bitbucket.org/mstrobel/procyon/issues/287/variables-incorerctly-merged\n//    @AssertNoWarning(\"SelfComputation\")\n//    public int appendDigits(long num, int maxdigits) {\n//        char[] buf = new char[maxdigits];\n//        int ix = maxdigits;\n//        while (ix > 0) {\n//            buf[--ix] = '0';\n//        }\n//        return maxdigits - ix;\n//    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSerializationIdiom.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.util.Comparator;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestSerializationIdiom implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    @AssertWarning(\"ComparatorIsNotSerializable\")\n    public class MyComparator implements Comparator<String> {\n        @Override\n        public int compare(String o1, String o2) {\n            return o1.toLowerCase().compareTo(o2.toLowerCase());\n        }\n\n        @AssertNoWarning(\"SerializationMethodMustBePrivate\")\n        void readObjectNoData() {\n        }\n    }\n\n    @SuppressWarnings(\"serial\")\n    public static class NonStaticUid implements Serializable {\n        @AssertWarning(\"SerialVersionUidNotStatic\")\n        final long serialVersionUID = 1L;\n    }\n\n    @SuppressWarnings(\"serial\")\n    public static class NonFinalUid implements Serializable {\n        @AssertWarning(\"SerialVersionUidNotFinal\")\n        static long serialVersionUID = 1L;\n    }\n\n    @SuppressWarnings(\"serial\")\n    public static class NonLongUid implements Serializable {\n        @AssertWarning(\"SerialVersionUidNotLong\")\n        static final int serialVersionUID = 1;\n    }\n\n    @AssertWarning(\"SerializationMethodMustBePrivate\")\n    void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {\n        ois.defaultReadObject();\n    }\n\n    @AssertWarning(\"SerializationMethodMustBePrivate\")\n    public void readObjectNoData() {\n    }\n\n    @AssertWarning(\"SerializationMethodMustBePrivate\")\n    protected void writeObject(ObjectOutputStream oos) throws IOException {\n        oos.defaultWriteObject();\n    }\n\n    public static class ReadResolveStatic implements Serializable {\n        private static final long serialVersionUID = 1L;\n\n        @AssertWarning(\"ReadResolveIsStatic\")\n        static Object readResolve() {\n            return \"xyz\";\n        }\n    }\n\n    public static class ReadResolveNonObject implements Serializable {\n        private static final long serialVersionUID = 1L;\n\n        @AssertWarning(\"ReadResolveMustReturnObject\")\n        private String readResolve() {\n            return \"xyz\";\n        }\n    }\n\n    public static class ReadObjectSynchronized implements Serializable {\n        private static final long serialVersionUID = 1L;\n\n        @AssertWarning(\"ReadObjectIsSynchronized\")\n        private synchronized void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {\n            ois.defaultReadObject();\n        }\n    }\n    \n    public static class WriteObjectSynchronized implements Serializable {\n        private static final long serialVersionUID = 1L;\n        \n        @AssertWarning(\"WriteObjectIsSynchronized\")\n        private synchronized void writeObject(ObjectOutputStream oos) throws IOException {\n            oos.defaultWriteObject();\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSpinLoop.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestSpinLoop {\n    boolean flag;\n\n    volatile boolean vflag;\n\n    @AssertWarning(\"SpinLoopOnField\")\n    void waitForTrue() {\n        while (flag)\n            ;\n    }\n\n    @AssertNoWarning(\"SpinLoopOnField\")\n    void waitForVolatileTrue() {\n        while (vflag)\n            ;\n    }\n\n    TestSpinLoop foo;\n\n    TestSpinLoop bar;\n\n    @AssertWarning(\"SpinLoopOnField\")\n    void waitForNonNull() {\n        while (foo == null)\n            ;\n    }\n\n    @AssertWarning(\"SpinLoopOnField\")\n    static void waitForNonNullIndirect(int x, TestSpinLoop baz) {\n        while (baz.foo == null)\n            ;\n    }\n\n    @AssertWarning(\"SpinLoopOnField\")\n    static void waitForNonNullIndirect2(int x, TestSpinLoop baz) {\n        while (baz.foo.bar == null)\n            ;\n    }\n\n    static boolean sflag;\n\n    @AssertWarning(\"SpinLoopOnField\")\n    static void waitForStatic() {\n        while (!sflag)\n            ;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSqlBadArgument.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestSqlBadArgument {\n    @AssertWarning(value=\"BadResultSetArgument\", minScore=70)\n    public String get(ResultSet rs) throws SQLException {\n        return rs.getString(0);\n    }\n\n    @AssertWarning(value=\"BadResultSetArgument\", maxScore=60)\n    public String getConditional(ResultSet rs, boolean b) throws SQLException {\n        int pos = b ? 2 : 0;\n        return rs.getString(pos);\n    }\n    \n    @AssertWarning(\"BadPreparedStatementArgument\")\n    public void set(PreparedStatement ps) throws SQLException {\n        ps.setInt(0, 10);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void setOk(PreparedStatement ps) throws SQLException {\n        ps.setInt(10, 0);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestStartInConstructor.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestStartInConstructor {\n    @AssertWarning(value=\"StartInConstructor\", minScore=45, maxScore=55)\n    public TestStartInConstructor() {\n        new Thread(() -> System.out.println()).start();\n        System.out.println(\"Started!\");\n    }\n    \n    @AssertWarning(value=\"StartInConstructor\", minScore=45, maxScore=55)\n    public TestStartInConstructor(String s, int x) {\n        new Thread() {\n            @Override\n            public void run() {\n                System.out.println(\"Thread!\");\n            }\n        }.start();\n        System.out.println(\"Started!\");\n    }\n    \n    @AssertWarning(value=\"StartInConstructor\", minScore=35, maxScore=45)\n    public TestStartInConstructor(int x) {\n        new Thread(() -> System.out.println()).start();\n    }\n    \n    @AssertNoWarning(\"StartInConstructor\")\n    private TestStartInConstructor(String s) {\n        new Thread(() -> System.out.println()).start();\n    }\n    \n    public class SubClass extends TestStartInConstructor {\n        @AssertWarning(value=\"StartInConstructor\", minScore=25, maxScore=35)\n        public SubClass(int x) {\n            new Thread(() -> System.out.println()).start();\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestStaticFieldFromInstanceMethod.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestStaticFieldFromInstanceMethod {\n    private static int val;\n    private static boolean VERBOSE;\n    private static boolean loggingSwitchedOff;\n    private static int debugLevel = 0;\n    static Object data;\n    \n    @AssertWarning(value=\"StaticFieldFromInstanceMethod\", minScore=55)\n    public void testWrite(int x) {\n        val = x;\n        System.out.println(val);\n    }\n\n    @AssertWarning(value=\"StaticFieldFromInstanceMethod\", minScore=35, maxScore=35)\n    private void testWritePrivate(int x) {\n        val = x;\n        System.out.println(val);\n    }\n    \n    @AssertWarning(value=\"StaticFieldFromInstanceMethod\", minScore=45, maxScore=45)\n    protected void testWriteProtected(int x) {\n        val = x;\n        System.out.println(val);\n    }\n    \n    @AssertWarning(value=\"StaticFieldFromInstanceMethod\", minScore=50, maxScore=50)\n    public void cleanUp() {\n        data = null;\n    }\n    \n    @AssertWarning(value=\"StaticFieldFromInstanceMethod\", minScore=35, maxScore=35)\n    public void cleanUpSynchronized() {\n        synchronized(TestStaticFieldFromInstanceMethod.class) {\n            data = null;\n        }\n    }\n\n    @AssertWarning(value=\"StaticFieldFromInstanceMethod\", minScore=35, maxScore=35)\n    public synchronized void cleanUpSynchronizedMethod() {\n        data = null;\n    }\n    \n    @AssertWarning(value=\"StaticFieldFromInstanceMethod\", minScore=40, maxScore=40)\n    public void setVerbose(boolean v) {\n        VERBOSE = v;\n    }\n\n    @AssertWarning(value=\"StaticFieldFromInstanceMethod\", minScore=45, maxScore=45)\n    public void log(String message) {\n        if(loggingSwitchedOff)\n            return;\n        try {\n            Class.forName(\"my.super.Logger\").getMethod(\"log\", String.class).invoke(null, message);\n        }\n        catch(Exception ex) {\n            loggingSwitchedOff = true;\n        }\n    }\n    \n    @AssertWarning(value=\"StaticFieldFromInstanceMethod\", minScore=40, maxScore=40)\n    public void setDebugLevel(int level) {\n        debugLevel = level;\n    }\n    \n    @AssertNoWarning(\"StaticFieldFromInstanceMethod\")\n    public static void testWriteFromStatic(int x) {\n        val = x;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestStaticFieldNonThreadSafe.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author shustkost\n *\n */\npublic class TestStaticFieldNonThreadSafe {\n    @AssertWarning(\"StaticNotThreadSafeField\")\n    public static final Calendar CALENDAR = Calendar.getInstance();\n    \n    @AssertWarning(value=\"StaticNotThreadSafeField\", maxScore=55)\n    protected static final SimpleDateFormat SDF = new SimpleDateFormat(\"dd\");\n    \n    @AssertNoWarning(\"*\")\n    private static final SimpleDateFormat privateSDF = new SimpleDateFormat(\"dd\");\n\n    @AssertNoWarning(\"*\")\n    private static final String date = privateSDF.format(new Date());\n    \n    private static final DateFormat usedSDF = new SimpleDateFormat(\"dd\");\n    \n    static {\n        System.out.println(date);\n    }\n    \n    @AssertWarning(\"StaticNotThreadSafeFieldInvoke\")\n    public String format(Date date) {\n        return usedSDF.format(date);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestStringConcatInLoop.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestStringConcatInLoop {\n    @AssertWarning(value=\"StringConcatInLoop\", minScore=47)\n    public String testStringConcatInLoopSimple(String[] data) {\n        String result = \"\";\n        for(String row : data)\n            result+=row;\n        return result;\n    }\n\n    @AssertWarning(value=\"StringConcatInLoop\", maxScore=47)\n    public String testStringConcatInLoopIf(String[] data) {\n        String result = \"\";\n        for(String row : data)\n            if(!row.isEmpty())\n                result+=row;\n        return result;\n    }\n\n    @AssertWarning(\"StringConcatInLoop\")\n    public String testStringConcatInLoopNested(String[] data) {\n        String result = \"\";\n        for(String row : data) {\n            for(int i=0; i<10; i++)\n                result+=row;\n        }\n        return result+ParseException.r;\n    }\n    \n    @AssertWarning(\"StringConcatInLoop\")\n    public int testStringConcatInLoopNested2(String[] data) {\n        int i=0;\n        for(String row : data) {\n            String result = \"\";\n            for(int j=0; j<10; j++)\n                result+=row;\n            i+=result.length();\n        }\n        return i;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public int testStringConcatOk(String[] data) {\n        int n = 0;\n        for(String row : data) {\n            String result = row; \n            result+=row;\n            n+=result.length();\n        }\n        return n;\n    }\n    \n    @AssertNoWarning(\"*\")\n    static class ParseException extends Exception {\n        public static final String r = initialise(new String[] {\"1\", \"2\"});\n        \n        private static String initialise(String[] tokens) {\n            String retval = \"\";\n            for (String t : tokens) {\n                retval += t;\n            }\n            return retval;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestStringIndex.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestStringIndex {\n    private static final String TEST_STRING = \" test string \";\n\n    @AssertWarning(\"StringIndexIsGreaterThanAllowed\")\n    public String trimSubstring() {\n        return TEST_STRING.trim().substring(TEST_STRING.length());\n    }\n    \n    @AssertWarning(\"UselessStringSubstring\")\n    public String uselessSubstring() {\n        return TEST_STRING.trim().substring(0);\n    }\n    \n    @AssertWarning(\"StringIndexIsLessThanZero\")\n    public String lessThanZero() {\n        return TEST_STRING.substring(-1);\n    }\n\n    @AssertWarning(\"StringIndexIsLessThanZero\")\n    public String lessThanZero2() {\n        return TEST_STRING.substring(1, -1);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public String substringCorrect() {\n        return TEST_STRING.substring(TEST_STRING.length())\n                + TEST_STRING.substring(TEST_STRING.length()-1)\n                +  TEST_STRING.substring(1, TEST_STRING.length())\n                +  TEST_STRING.substring(1, 2);\n    }\n    \n    @AssertWarning(\"StringIndexIsGreaterThanAllowed\")\n    public String substringIncorrect() {\n        return TEST_STRING.substring(TEST_STRING.length()+1);\n    }\n    @AssertWarning(\"StringIndexIsGreaterThanAllowed\")\n    public String substringIncorrect2() {\n        return TEST_STRING.substring(1, TEST_STRING.length()+1);\n    }\n    \n    @AssertWarning(\"StringIndexIsGreaterThanAllowed\")\n    public String substringIncorrect3() {\n        return TEST_STRING.substring(2, 1);\n    }\n    @AssertNoWarning(\"*\")\n    public char charAtCorrect() {\n        return TEST_STRING.charAt(TEST_STRING.length()-1);\n    }\n    \n    @AssertWarning(\"StringIndexIsGreaterThanAllowed\")\n    public char charAtIncorrect() {\n        return TEST_STRING.charAt(TEST_STRING.length());\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestStringUsage.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestStringUsage {\n    \n    @AssertWarning(value=\"StringConstructor\", maxScore=35)\n    public static final String STRING = new String(\"abc\");\n    \n    @AssertWarning(\"StringConstructorEmpty\")\n    public String testStringCtor() {\n        return new String();\n    }\n    \n    @AssertWarning(\"StringConstructor\")\n    public String testStringCtor2() {\n        return new String(\"test\");\n    }\n\n    @AssertNoWarning(\"*\")\n    public String testStringCtor3() {\n        return new String(new char[] {'c'});\n    }\n    \n    @AssertWarning(\"StringToString\")\n    public String testStringToString(String s) {\n        return s.toString();\n    }\n    \n    @AssertNoWarning(\"*\")\n    public String testObjectToString(Object s) {\n        if(s instanceof String)\n            return s.toString();\n        return null;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSwingProblems.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport javax.swing.JPanel;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestSwingProblems {\n    @AssertNoWarning(\"SwingMethodNotInSwingThread\")\n    private static void run() {\n        JPanel panel = new JPanel();\n        panel.setVisible(true);\n    }\n    \n    @AssertNoWarning(\"SwingMethodNotInSwingThread\")\n    public static void main(String[] args, boolean x) {\n        JPanel panel = new JPanel();\n        panel.setVisible(x && args.length > 0);\n    }\n    \n    @AssertWarning(\"SwingMethodNotInSwingThread\")\n    public static void main(String[] args) {\n        JPanel panel = new JPanel();\n        panel.setVisible(true);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSyncGetClass.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestSyncGetClass {\n    private static int val;\n    \n    @AssertWarning(value=\"SyncOnGetClass\", minScore=50)\n    public void update(int x) {\n        synchronized (getClass()) {\n            val = x;\n            System.out.println(val);\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    public static void updateStatic(TestSyncGetClass obj, int x) {\n        synchronized (obj.getClass()) {\n            val = x;\n            System.out.println(val);\n        }\n    }\n\n    @AssertNoWarning(\"SyncOnGetClass\")\n    @AssertWarning(\"StaticFieldFromInstanceMethod\")\n    public void update(Object obj, int x) {\n        synchronized (obj.getClass()) {\n            val = x;\n            System.out.println(val);\n        }\n    }\n\n    @AssertWarning(value=\"SyncOnGetClass\", minScore=40, maxScore=49)\n    public void print(int x) {\n        synchronized (getClass()) {\n            System.out.println(x);\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestSyncOnUpdatedField.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestSyncOnUpdatedField {\n    static Object globalLock = new Object();\n    \n    Object lock = new Object();\n    \n    @AssertWarning(\"SynchronizationOnUpdatedField\")\n    public void testSimpleSync() {\n        synchronized (lock) {\n            System.out.println(\"In lock\");\n            lock = new Object();\n        }\n    }\n    \n    @AssertWarning(\"SynchronizationOnUpdatedField\")\n    public void testSimpleStatic() {\n        synchronized (globalLock) {\n            System.out.println(\"In lock\");\n            globalLock = new Object();\n        }\n    }\n\n    @AssertWarning(\"SynchronizationOnUpdatedField\")\n    public void testOtherSync(TestSyncOnUpdatedField tsouf) {\n        synchronized (tsouf.lock) {\n            System.out.println(\"In lock\");\n            tsouf.lock = new Object();\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testOtherSyncOk(TestSyncOnUpdatedField tsouf) {\n        synchronized (tsouf.lock) {\n            System.out.println(\"In lock\");\n            lock = new Object();\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void testOtherSyncOk2(TestSyncOnUpdatedField tsouf) {\n        synchronized (tsouf.lock) {\n            tsouf = this;\n            System.out.println(\"In lock\");\n            tsouf.lock = new Object();\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestToArrayDowncast.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestToArrayDowncast {\n    @AssertWarning(\"ImpossibleToArrayDowncast\")\n    public String[] toArray(ArrayList<String> l) {\n        return (String[]) l.toArray();\n    }\n\n    @AssertWarning(\"ImpossibleToArrayDowncast\")\n    public String[] toArray(Collection<String> l) {\n        return (String[]) l.toArray();\n    }\n    \n    @AssertNoWarning(\"ImpossibleToArrayDowncast\")\n    public String[] toArrayOk(Collection<String> l) {\n        return l.toArray(new String[0]);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    // Probably some other warning should be issued here, but not this\n    @AssertNoWarning(\"ImpossibleToArrayDowncast\")\n    public <T> T[] toArrayGeneric(Collection<String> l) {\n        return (T[])l.toArray();\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestUncalledPrivateMethod.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.stream.Stream;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestUncalledPrivateMethod {\n    @interface MyAnno {\n    }\n\n    @AssertWarning(\"UncalledPrivateMethod\")\n    private void simple() {\n        System.out.println(\"Uncalled\");\n    }\n\n    @AssertWarning(\"UncalledPrivateMethod\")\n    @Deprecated\n    private void deprecated() {\n        System.out.println(\"Uncalled\");\n    }\n\n    @AssertNoWarning(\"UncalledPrivateMethod\")\n    @MyAnno\n    private void annotated() {\n        System.out.println(\"Uncalled\");\n    }\n\n    @AssertNoWarning(\"UncalledPrivateMethod\")\n    void packagePrivate() {\n        System.out.println(\"Uncalled\");\n    }\n\n    @AssertNoWarning(\"UncalledPrivateMethod\")\n    private void called() {\n        System.out.println(\"Called\");\n    }\n\n    @AssertNoWarning(\"UncalledPrivateMethod\")\n    private void methodRef(String s) {\n        System.out.println(\"Called \" + s);\n    }\n\n    @AssertNoWarning(\"UncalledPrivateMethod\")\n    private void lambda(String s) {\n        System.out.println(\"Called \" + s);\n    }\n    \n    public void caller() {\n        called();\n        Stream.of(\"a\").forEach(this::methodRef);\n        Stream.of(\"b\").forEach(b -> this.lambda(b));\n    }\n    \n    @AssertWarning(\"UncalledPrivateMethodChain\")\n    @AssertNoWarning(\"UncalledPrivateMethod\")\n    private void callA() {\n        callB();\n    }\n    \n    @AssertNoWarning(\"UncalledPrivateMethod\")\n    private void callB() {\n        callA();\n    }\n    \n    @AssertWarning(\"UncalledPrivateMethodChain\")\n    @AssertNoWarning(\"UncalledPrivateMethod\")\n    private void selfLambda() {\n        Runnable r = () -> selfLambda();\n        r.run();\n        this.r.run();\n    }\n    \n    Runnable r = new Runnable() {\n        @AssertWarning(\"UncalledMethodOfAnonymousClass\")\n        public void test() {\n            System.out.println(\"test\");\n        }\n\n        @AssertNoWarning(\"*\")\n        public void test2() {\n            System.out.println(\"test2\");\n        }\n        \n        @AssertNoWarning(\"*\")\n        public void test3() {\n            System.out.println(\"test3\");\n        }\n        \n        @Override\n        public void run() {\n            System.out.println(\"run\");\n            test2();\n            new Runnable() {\n                @Override\n                public void run() {\n                    test3();\n                }\n            }.run();\n        }\n    };\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestUnnecessaryBoxing.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestUnnecessaryBoxing {\n    @AssertWarning(\"BoxedForToString\")\n    public String getString(int a, int b) {\n        Integer integer = a + b;\n        return integer.toString();\n    }\n\n    @AssertWarning(\"BoxedForToString\")\n    public String getString(double a, double b) {\n        return new Double(a + b).toString();\n    }\n    \n    @AssertNoWarning(\"BoxedForToString\")\n    public String getStringNoToString(int a, int b) {\n        Integer sum = a + b;\n        return Integer.toString(sum);\n    }\n\n    @AssertWarning(\"BoxedForUnboxing\")\n    public int boxUnbox(int a, int b) {\n        Integer result = a + b;\n        return result;\n    }\n    \n    @AssertWarning(value=\"UnboxedForBoxing\", minScore=40)\n    public Integer unboxBox(Integer x) {\n        int a = x;\n        return a;\n    }\n\n    @AssertNoWarning(\"UnboxedForBoxing\")\n    public Integer unboxBox(Character x) {\n        int a = x;\n        return a;\n    }\n    \n    @AssertWarning(value=\"UnboxedForBoxing\", maxScore=30)\n    public Boolean unboxBox(Boolean x) {\n        boolean a = x;\n        return a;\n    }\n    \n    @AssertWarning(\"UnboxedForBoxing\")\n    public Integer unboxBoxTwice(Integer x) {\n        int a = x;\n        if (x > 2)\n            return unboxBox(a);\n        return a;\n    }\n\n    @AssertNoWarning(\"UnboxedForBoxing\")\n    public Integer unboxBoxOk(Integer x) {\n        int a = x;\n        if (x > 2)\n            return Math.abs(a);\n        return a;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestUnnecessaryInstanceOf.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestUnnecessaryInstanceOf {\n    Object f = Math.random() > 0.5 ? (Number) 1 : (Number) 1.0;\n\n    @AssertWarning(value = \"UnnecessaryInstanceOf\", maxScore = 55)\n    void testInferred(int x) {\n        Object a = 1.0;\n        if (x > 2)\n            a = -2;\n        if (a instanceof Number) {\n            System.out.println(a);\n        }\n    }\n    \n    @AssertWarning(value = \"UnnecessaryInstanceOf\")\n    void testBooleanInstanceOf(Object obj) {\n        boolean flag = obj instanceof String;\n        if(flag) {\n            if(obj instanceof CharSequence) {\n                System.out.print(\"ok\");\n            }\n        }\n    }\n\n    @AssertWarning(\"UnnecessaryInstanceOf\")\n    void testInferredDeadCode(int x) {\n        Object a = 1.0;\n        if (x > 2)\n            a = -2;\n        if (a instanceof Number) {\n            System.out.println(a);\n        } else {\n            System.out.println(\"Never\");\n        }\n    }\n    \n    @AssertWarning(\"UnnecessaryInstanceOf\")\n    void testField() {\n        if (f instanceof Number) {\n            System.out.println(f);\n        }\n    }\n    \n    @AssertWarning(value=\"ClassComparisonFalse\", minScore=65)\n    void testClassComparisonSimple(String s) {\n        Object obj = s;\n        if(obj.getClass() == Integer.class) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @AssertWarning(\"ClassComparisonFalse\")\n    void testClassComparisonMixed(String a, Number b) {\n        if(b instanceof Float) {\n            Object x = Math.random() > 0.5 ? a : b;\n            if(x.getClass() == Long.class) {\n                System.out.println(\"Never\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"ClassComparisonFalse\")\n    void testClassComparisonComplex(String a, Number b) {\n        Object x = 1.0;\n        if(b instanceof Float) {\n            x = Math.random() > 0.5 ? a : b;\n        }\n        if(x.getClass() == Long.class) {\n            System.out.println(\"Never\");\n        }\n    }\n    \n    @AssertWarning(\"UnnecessaryInstanceOf\")\n    void testSimple() {\n        String a = \"test\";\n        if (a instanceof CharSequence) {\n            System.out.println(a);\n        }\n    }\n\n    @AssertWarning(\"ImpossibleCast\")\n    int testComplex(Object obj) {\n        if(obj instanceof Integer)\n            return 2;\n        if(obj instanceof String)\n            return 1;\n        if(obj instanceof CharSequence)\n            return 3;\n        return Integer.valueOf((String)obj);\n    }\n\n    @AssertWarning(\"ImpossibleCast\")\n    void testCast() {\n        Object a = \"test\";\n        System.out.println((Integer) a);\n    }\n\n    @AssertWarning(\"ImpossibleInstanceOf\")\n    void testArray(String[] data) {\n        Object[] arr = data;\n        if (arr instanceof Integer[]) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    int testPrimArray(Object data) {\n        if (data instanceof Object[])\n            return 1;\n        if (data instanceof int[])\n            return 2;\n        return 0;\n    }\n\n    @AssertWarning(\"UnnecessaryInstanceOf\")\n    void testArrayOk(String[] data) {\n        Object[] arr = data;\n        if (arr instanceof CharSequence[]) {\n            System.out.println(\"Always\");\n        }\n    }\n\n    @AssertWarning(\"UnnecessaryInstanceOf\")\n    void testConditional(Object obj) {\n        if (!(obj instanceof String))\n            return;\n        if (obj instanceof CharSequence) {\n            System.out.println(\"Always\");\n        }\n    }\n\n    @AssertWarning(\"ImpossibleInstanceOf\")\n    void testCCE(Object obj) {\n        CharSequence s;\n        try {\n            s = (CharSequence) obj;\n        } catch (ClassCastException cce) {\n            if (obj instanceof String) {\n                System.out.println(\"Never!\");\n            }\n            return;\n        }\n        System.out.println(s);\n    }\n\n    @AssertWarning(\"ImpossibleInstanceOf\")\n    void testConditionalImpossible(Object obj) {\n        if (!(obj instanceof String))\n            return;\n        if (obj instanceof Number) {\n            System.out.println(\"Never\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    void testInterfaceNonFinal(ArrayList<String> al) {\n        Object obj = al;\n        if (obj instanceof Comparable) {\n            System.out.println(\"Yes!\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    void testInterfaceNonFinal2(Comparable<?> cmp) {\n        Object obj = cmp;\n        if (obj instanceof ArrayList) {\n            System.out.println(\"Yes!\");\n        }\n    }\n\n    @AssertWarning(\"ImpossibleInstanceOf\")\n    void testInterfaceFinal(StringBuilder sb) {\n        Object obj = sb;\n        if (obj instanceof Comparable) {\n            System.out.println(\"Yes!\");\n        }\n    }\n\n    @AssertNoWarning(\"*\")\n    void testTypeMerging(Object obj, String type) {\n        if (type.equals(\"String\")) {\n            String val = (String) obj;\n            System.out.println(\"String: \" + val);\n        }\n        if (type.equals(\"Int\")) {\n            Integer val = (Integer) obj;\n            System.out.println(\"Int: \" + val);\n        }\n    }\n\n    String[] stringArr() {\n        return new String[] { \"test\" };\n    }\n\n    int[] intArr() {\n        return new int[] { 1 };\n    }\n    \n    @AssertWarning(\"UnnecessaryInstanceOf\")\n    void testRetValue1() {\n        Object x = stringArr();\n        if(x instanceof String[]) {\n            System.out.println(\"Ok\");\n        }\n    }\n    \n    @AssertWarning(\"UnnecessaryInstanceOf\")\n    void testRetValue2() {\n        Object x = intArr();\n        if(x instanceof int[]) {\n            System.out.println(\"Ok\");\n        }\n    }\n    \n    @AssertWarning(\"ImpossibleInstanceOf\")\n    void testGetClass(Object obj) {\n        if(obj.getClass() == ArrayList.class) {\n            if(obj instanceof Comparable) {\n                System.out.println(\"Never\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"ImpossibleInstanceOf\")\n    void testGetClass2(Object obj) {\n        if(ArrayList.class.equals(obj.getClass())) {\n            if(obj instanceof Comparable) {\n                System.out.println(\"Never\");\n            }\n        }\n    }\n    \n    @AssertWarning(\"UnnecessaryInstanceOf\")\n    void testIsInstance(Object obj) {\n        Class<?> clazz = String.class;\n        if(clazz.isInstance(obj)) {\n            if(obj instanceof CharSequence) {\n                System.out.println(\"Always\");\n            }\n        }\n    }\n    \n    Object convert(String s) {\n        return Integer.valueOf(s);\n    }\n    \n    @AssertNoWarning(\"*\")\n    void testTwoOptions(Object obj) {\n        if(obj == null) {\n            obj = \"1\";\n        }\n        if(obj instanceof String) {\n            String s = (String)obj;\n            System.out.println(s);\n            obj = convert(s);\n        }\n        Integer i = (Integer)obj;\n        System.out.println(i);\n    }\n    \n    public class MyList<E extends CharSequence> extends ArrayList<E> {\n        private static final long serialVersionUID = 1L;\n        \n        @AssertNoWarning(\"*\")\n        public boolean hasString() {\n            for(Iterator<E> itr = iterator(); itr.hasNext();) {\n                CharSequence cs = itr.next();\n                if(cs instanceof String) {\n                    return true;\n                }\n            }\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestUnreachableCatch.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestUnreachableCatch {\n    @AssertNoWarning(\"*\")\n    public String testOk(Object x) {\n        try {\n            return (String)x;\n        }\n        catch(ClassCastException cce) {\n            cce.printStackTrace();\n            return null;\n        }\n    }\n    \n    @AssertWarning(\"UnreachableCatch\")\n    public Object testUnreachable(Object x) {\n        try {\n            return x;\n        }\n        catch(ClassCastException cce) {\n            cce.printStackTrace();\n            return null;\n        }\n    }\n\n    @AssertWarning(\"UnreachableCatch\")\n    public Object testUnreachableMulti(Object x) {\n        try {\n            return x;\n        }\n        catch(ClassCastException | NullPointerException ex) {\n            ex.printStackTrace();\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestUnsafeGetResource.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.io.InputStream;\nimport java.net.URL;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestUnsafeGetResource {\n    @AssertWarning(value=\"UnsafeGetResource\", minScore=50)\n    public URL getURL() {\n        Class<?> myClass = getClass();\n        return myClass.getResource(\"foo\");\n    }\n\n    @AssertNoWarning(\"UnsafeGetResource\")\n    public URL getURLOk() {\n        return TestUnsafeGetResource.class.getResource(\"foo\");\n    }\n    \n    @AssertWarning(value=\"UnsafeGetResource\", minScore=50)\n    public InputStream openResource() {\n        return getClass().getResourceAsStream(\"foo\");\n    }\n\n    @AssertWarning(value=\"UnsafeGetResource\", maxScore=35)\n    public InputStream openResourceFromRoot() {\n        return getClass().getResourceAsStream(\"/foo\");\n    }\n    \n    public static class NoSubClasses {\n        @AssertWarning(value=\"UnsafeGetResource\", minScore=40, maxScore=40)\n        public URL getURL() {\n            return getClass().getResource(\"foo\");\n        }\n    }\n    \n    public static final class SubClass extends TestUnsafeGetResource {\n        @AssertNoWarning(\"UnsafeGetResource\")\n        public URL getURL2() {\n            return getClass().getResource(\"foo\");\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestUnsupportedCall.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Set;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestUnsupportedCall {\n    public TestUnsupportedCall() {\n        throw Math.random() > 0.5 ? new UnsupportedOperationException() : new UnsupportedOperationException(\"With message\");\n    }\n\n    public TestUnsupportedCall(int x) {\n        System.out.println(x);\n    }\n    \n    @AssertWarning(\"UnsupportedCall\")\n    public void hello(int x) {\n        doHello(x+1);\n    }\n\n    @AssertNoWarning(\"*\")\n    private void doHello(int i) {\n        throw new UnsupportedOperationException();\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void addAll(Set<String> a, Set<String> b) {\n        a.addAll(b);\n    }\n    \n    @AssertWarning(\"UnsupportedCall\")\n    public static void test() {\n        new TestUnsupportedCall();\n    }\n    \n    @AssertNoWarning(\"UnsupportedCall\")\n    public static void testCatch() {\n        try {\n            new TestUnsupportedCall();\n        } catch (UnsupportedOperationException e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestUnusedParameter.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author shustkost\n *\n */\npublic abstract class TestUnusedParameter {\n    @AssertNoWarning(\"*\")\n    public TestUnusedParameter(int x, int y) {\n        this(x, y, \"other\");\n    }\n\n    @AssertNoWarning(\"*\")\n    public TestUnusedParameter() {\n        this(1, 2, \"other\");\n    }\n    \n    @AssertWarning(\"ConstructorParameterIsNotPassed\")\n    public TestUnusedParameter(int x, String z) {\n        this(x, 2, \"other\");\n    }\n    \n    @AssertNoWarning(\"*\")\n    public TestUnusedParameter(int x, int y, String z) {\n        System.out.println(x + y + z);\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void print(String info, int x) {\n        System.out.println(info+\" \"+x);\n    }\n    \n    @AssertWarning(\"MethodParameterIsNotPassed\")\n    public void print(int x) {\n        print(\"test\", 0);\n    }\n    \n    @AssertNoWarning(\"MethodParameterIsNotPassed\")\n    @AssertWarning(\"MethodParameterIsNotUsed\")\n    public void print(double x) {\n        print(\"test\", 0);\n    }\n\n    @AssertNoWarning(\"*\")\n    public String test(int x, int y) {\n        return null;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public int test2(int x, int y) {\n        return 0;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public boolean test3(int x, int y) {\n        return false;\n    }\n    \n    @AssertNoWarning(\"*\")\n    public void test4(int x, int y) {\n    }\n    \n    public static void printStatic(String info, int x) {\n        System.out.println(info+\" \"+x);\n    }\n    \n    @AssertWarning(\"MethodParameterIsNotPassed\")\n    @AssertNoWarning(\"MethodParameterIsNotUsed\")\n    public static void printStatic(int x) {\n        printStatic(\"test\", 0);\n    }\n    \n    @AssertNoWarning(\"MethodParameterIsNotPassed\")\n    @AssertWarning(\"MethodParameterIsNotUsed\")\n    public void printStatic(double x) {\n        printStatic(\"test\", 0);\n    }\n    \n    @AssertNoWarning(\"*\")\n    abstract protected void test(int x);\n    \n    static class Xyz extends TestUnusedParameter {\n        @AssertNoWarning(\"*\")\n        @Override\n        protected void test(int x) {\n            System.out.println(\"Ok\");            \n        }\n    }\n    \n    static class Generic<T> {\n        @AssertNoWarning(\"*\")\n        protected void foo(T param) {\n            System.out.println(\"foo\");\n        }\n    }\n    \n    static class SubClass extends Generic<String> {\n        @Override\n        @AssertNoWarning(\"*\")\n        protected void foo(String param) {\n            System.out.println(\"bar\");\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestUselessVoidMethod.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestUselessVoidMethod {\n    @AssertWarning(\"UselessVoidMethod\")\n    public void doNothing(int x) {\n        int y = x;\n        if (x > 0) {\n            y = y * 2;\n        }\n    }\n    \n    @AssertWarning(\"UselessVoidMethod\")\n    public void uselessSwitch(int x) {\n        switch(x) {\n        case 1:\n            break;\n        }\n    }\n    \n    boolean b;\n    volatile boolean v;\n    \n    @AssertWarning(\"UselessVoidMethod\")\n    public void uselessSpinLoop() {\n        while(b) {}\n    }\n    \n    @AssertNoWarning(\"UselessVoidMethod\")\n    public void volatileSpinLoop() {\n        while(v) {}\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestValuesFlow.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.Hashtable;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestValuesFlow {\n    @AssertNoWarning(\"*\")\n    void testForSwitch(Map<Character, Integer> mod) {\n        Map<Character, Integer> mapTemp = new Hashtable<>();\n        for (Entry<Character, Integer> e : mod.entrySet()) {\n            Character key = e.getKey();\n            switch (key) {\n            case 'B':\n                // we will phase these at the time of rotation, in setModRot\n                break;\n            case 'C':\n                // not implemented\n                continue;\n            }\n            mapTemp.put(key, e.getValue());\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestVolatileArray.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author lan\n *\n */\npublic class TestVolatileArray {\n    @AssertWarning(\"FieldIsAlwaysNull\")\n    @AssertNoWarning(\"VolatileArray\")\n    public volatile Object[] a = null;\n\n    @AssertWarning(value = \"VolatileArray\", minScore = 50)\n    public volatile int[] b = { 1, 2, 3 };\n\n    @AssertWarning(value = \"VolatileArray\", maxScore = 45)\n    public volatile long[] c = { 1, 2, 3 };\n    \n    public static class AnotherClass {\n        public void set(TestVolatileArray tva, long[] arr) {\n            tva.c = arr;\n            if(tva.a != null) {\n                System.out.println(tva.a);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestVolatileIncrement.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestVolatileIncrement {\n    volatile int x;\n    volatile long y;\n    volatile double z;\n    int a;\n    long b;\n    double c;\n\n    @AssertNoWarning(\"Volatile*\")\n    public int testNonVolatile() {\n        a++;\n        ++b;\n        c*=2;\n        return (int) (a+b+c);\n    }\n\n    @AssertWarning(value=\"VolatileIncrement\", minScore = 70)\n    public int testPre() {\n        return x += 2;\n    }\n    \n    @AssertWarning(value=\"VolatileIncrement\", minScore = 70)\n    public int testPost() {\n        return ++x;\n    }\n\n    @AssertWarning(value=\"VolatileIncrement\", minScore = 40, maxScore = 50)\n    public synchronized int testPostSynchronized() {\n        return ++x;\n    }\n    \n    @AssertWarning(value=\"VolatileIncrement\", minScore = 40, maxScore = 50)\n    public int testPostSynchronizedBlock() {\n        synchronized (this) {\n            return ++x;\n        }\n    }\n    \n    @AssertWarning(value=\"VolatileMath\", minScore = 70)\n    public int testMul() {\n        return x *= 2;\n    }\n\n    @AssertWarning(value=\"VolatileMath\", minScore = 80)\n    public double testMulDouble() {\n        z *= 3;\n        return z *= 2;\n    }\n\n    @AssertWarning(value=\"VolatileIncrement\", minScore = 80)\n    public long testPreLong() {\n        return y += 2;\n    }\n\n    @AssertWarning(value=\"VolatileIncrement\", minScore = 80)\n    public long testPostLong() {\n        return ++y;\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestWaitContract.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestWaitContract {\n    private boolean condition;\n\n    private final Object object = new Object();\n\n    @AssertNoWarning(\"NotifyNaked\")\n    public void setCondition() {\n        synchronized (object) {\n            condition = true;\n            object.notifyAll();\n        }\n    }\n\n    @AssertWarning(\"WaitUnconditional\")\n    public void noLoopOrTest() throws Exception {\n\n        synchronized (object) {\n            object.wait();\n        }\n    }\n\n    @AssertWarning(\"NotifyNaked\")\n    public void nakedNotify() throws Exception {\n        synchronized (object) {\n            object.notify();\n        }\n    }\n    \n    @AssertWarning(\"WaitNotInLoop\")\n    public void noLoop() throws Exception {\n\n        synchronized (object) {\n            if (!condition)\n                object.wait();\n        }\n    }\n\n    @AssertNoWarning(\"Wait*\")\n    public void whileDo() throws Exception {\n\n        synchronized (object) {\n            while (!condition) {\n                object.wait();\n            }\n        }\n    }\n\n    @AssertWarning(\"WaitUnconditional\")\n    public void doWhile() throws Exception {\n\n        synchronized (object) {\n            do {\n                object.wait();\n            } while (!condition);\n        }\n    }\n    \n    @AssertNoWarning(\"*\")\n    static class Holder {\n        private final Object object = new Object();\n\n        @AssertWarning(\"WaitUnconditional\")\n        class SubClass {\n            public void check() throws InterruptedException {\n                object.wait();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/TestWrongMapIterator.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata;\n\nimport java.util.EnumMap;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.TimeUnit;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\n/**\n * @author Tagir Valeev\n *\n */\npublic class TestWrongMapIterator {\n    @AssertWarning(value=\"WrongMapIterator\", minScore=45)\n    public void test(Map<String, String> m) {\n        Iterator<String> it = m.keySet().iterator();\n        while (it.hasNext()) {\n            String name = it.next();\n            String value = m.get(name);\n            System.out.println(name + \" = \" + value);\n        }\n    }\n\n    @AssertWarning(value=\"WrongMapIterator\", maxScore=35)\n    public void testNoLoop(Map<String, String> m) {\n        Iterator<String> it = m.keySet().iterator();\n        if (it.hasNext()) {\n            String name = it.next();\n            String value = m.get(name);\n            System.out.println(name + \" = \" + value);\n        }\n    }\n    \n    @AssertWarning(\"WrongMapIteratorValues\")\n    public void testValues(Map<String, String> m) {\n        Iterator<String> it = m.keySet().iterator();\n        while (it.hasNext()) {\n            String name = it.next();\n            String value = m.get(name);\n            System.out.println(\"Value = \" + value);\n        }\n    }\n    \n    @AssertWarning(\"WrongMapIterator\")\n    public void test2(int a, int b, int c, int d, Map<String, String> m) {\n        Iterator<String> it = m.keySet().iterator();\n        while (it.hasNext()) {\n            String name = it.next();\n            String value = m.get(name);\n            System.out.println(name + \" = \" + value);\n        }\n    }\n\n    @AssertWarning(\"WrongMapIterator\")\n    public void test3(Map<String, String> m) {\n        Set<String> s = m.keySet();\n        Iterator<String> it = s.iterator();\n        while (it.hasNext()) {\n            String name = it.next();\n            String value = m.get(name);\n            System.out.println(name + \" = \" + value);\n        }\n    }\n\n    @AssertWarning(\"WrongMapIterator\")\n    public void testFor(Map<String, String> m) {\n        for(String name : m.keySet()) {\n            String value = m.get(name);\n            System.out.println(name + \" = \" + value);\n        }\n    }\n    \n    @AssertWarning(\"WrongMapIterator\")\n    public void testForBoxing(Map<Integer, String> m) {\n        for(int key : m.keySet()) {\n            String value = m.get(key);\n            System.out.println(key + \" = \" + value);\n        }\n    }\n    \n    @AssertWarning(\"WrongMapIterator\")\n    public void test4(Map<String, String> m) {\n        Iterator<String> it = m.keySet().iterator();\n        while (it.hasNext()) {\n            Object name = it.next();\n            String value = m.get(name);\n            System.out.println(name.toString() + \" = \" + value);\n        }\n    }\n\n    @AssertNoWarning(\"WrongMapIterator\")\n    @AssertWarning(\"UnusedLocalVariable\")\n    public void testIteratorLoadBug(Map<String, String> m1, List<String> list) {\n        Set<String> keys = m1.keySet();\n        //int a = 0;\n        for(String str : list) {\n            System.out.println(m1.get(str));\n        }\n    }\n\n    @AssertNoWarning(\"WrongMapIterator\")\n    public void testRegisterReuseBug(Map<String, String> m) {\n        for(String key : m.keySet()) {\n            System.out.println(key);\n        }\n        String k = \"special\";\n        System.out.println(m.get(k));\n    }\n\n    @AssertNoWarning(\"*\")\n    public void testEnumMap(EnumMap<TimeUnit, String> m) {\n        for(TimeUnit key : m.keySet()) {\n            System.out.println(m.get(key));\n        }\n    }\n    \n    Map<TimeUnit, String> enumField = new EnumMap<>(TimeUnit.class);\n\n    @AssertNoWarning(\"*\")\n    public void testEnumMapField() {\n        for(TimeUnit key : enumField.keySet()) {\n            System.out.println(enumField.get(key));\n        }\n    }\n    \n    @AssertNoWarning(\"WrongMapIterator\")\n    public void testRegisterReuseBug2(Map<Integer, String> m) {\n        int maxKey = 0;\n        for(Integer key : m.keySet()) {\n            if(key > maxKey)\n                maxKey = key;\n        }\n        for(Integer i=0; i<maxKey; i++) {\n            String val = m.get(i);\n            System.out.println(i+\": \"+(val == null ? \"none\" : val));\n        }\n    }\n\n    @AssertWarning(\"WrongMapIterator\")\n    public void testTwice(Map<String, String> m1, Map<String, String> m2) {\n        for(String m1key : m1.keySet()) {\n            System.out.println(m1key);\n        }\n        for(String m2key : m2.keySet()) {\n            System.out.println(m2key+\"=\"+m2.get(m2key));\n        }\n    }\n\n    @AssertWarning(\"WrongMapIterator\")\n    public void testSingleElement(Map<String, String> m) {\n        String key = m.keySet().iterator().next();\n        String value = m.get(key);\n        System.out.println(key+\"=\"+value);\n    }\n\n    @AssertWarning(\"WrongMapIterator\")\n    public void testBoxing(Map<Integer, String> m) {\n        for(int key : m.keySet()) {\n            System.out.println(key+\": \"+m.get(key));\n        }\n    }\n\n    private final Map<Object, String> fieldMap = new HashMap<>();\n\n    @AssertWarning(\"WrongMapIterator\")\n    public void testField() {\n        Iterator<Object> it = fieldMap.keySet().iterator();\n        while (it.hasNext()) {\n            Object name = it.next();\n            String value = fieldMap.get(name);\n            System.out.println(name.toString() + \" = \" + value);\n        }\n    }\n\n    private static Map<Object, String> staticMap = new HashMap<>();\n\n    @AssertWarning(\"WrongMapIterator\")\n    public void testStatic() {\n        for(Object name : staticMap.keySet()) {\n            String value = staticMap.get(name);\n            System.out.println(name.toString() + \" = \" + value);\n        }\n    }\n\n    private final Map<Object, String> fieldMap2 = new HashMap<>();\n    @AssertNoWarning(\"WrongMapIterator\")\n    public void testWrongMap() {\n        Iterator<Object> it = fieldMap.keySet().iterator();\n        while (it.hasNext()) {\n            Object name = it.next();\n            String value = fieldMap2.get(name);\n            System.out.println(name.toString() + \" = \" + value);\n        }\n    }\n\n    public static class DebugHashMap extends HashMap<Object, String> {\n        @AssertWarning(\"WrongMapIterator\")\n        public void dump() {\n            Iterator<Object> it = keySet().iterator();\n            while (it.hasNext()) {\n                Object name = it.next();\n                String value = get(name);\n                System.out.println(name.toString() + \" = \" + value);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/testdata/sub/SubFieldAccess.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.testdata.sub;\n\nimport one.util.huntbugs.testdata.TestFieldAccess;\n\n/**\n * @author lan\n *\n */\npublic class SubFieldAccess {\n    int val = TestFieldAccess.FieldInterface.usedArr[1];\n    \n    Object val2 = TestFieldAccess.usedEverywhere;\n    \n    static {\n        System.out.println(TestFieldAccess.FieldInterface.emptyStrings);\n        System.out.println(TestFieldAccess.FieldInterface.strings);\n        System.out.println(TestFieldAccess.FieldInterface.stringsList);\n    }\n}\n"
  },
  {
    "path": "huntbugs/src/test/java/one/util/huntbugs/util/TestIterables.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.util;\n\nimport static org.junit.Assert.*;\n\nimport java.util.Arrays;\n\nimport org.junit.Test;\n\n/**\n * @author lan\n *\n */\npublic class TestIterables {\n    @Test\n    public void testConcat() {\n        assertEquals(Arrays.asList(1, 2, 3), Iterables\n                .toList(Iterables.concat(Arrays.asList(1, 2, 3), Arrays.asList())));\n        assertEquals(Arrays.asList(1, 2, 3), Iterables\n                .toList(Iterables.concat(Arrays.asList(), Arrays.asList(1, 2, 3))));\n        assertEquals(Arrays.asList(), Iterables.toList(Iterables.concat(Arrays.asList(), Arrays.asList())));\n        assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6), Iterables.toList(Iterables.concat(Arrays.asList(1, 2, 3), Arrays\n                .asList(4, 5, 6))));\n    }\n}\n"
  },
  {
    "path": "huntbugs-ant-plugin/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "huntbugs-ant-plugin/.gitignore",
    "content": "/target/\n"
  },
  {
    "path": "huntbugs-ant-plugin/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>huntbugs-ant-plugin</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "huntbugs-ant-plugin/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "huntbugs-ant-plugin/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "huntbugs-ant-plugin/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "huntbugs-ant-plugin/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n\n <parent>\n  <groupId>one.util</groupId>\n  <artifactId>huntbugs-all</artifactId>\n  <version>0.0.12-SNAPSHOT</version>\n </parent>\n <artifactId>huntbugs-ant-plugin</artifactId>\n <packaging>jar</packaging>\n\n <name>huntbugs-ant-plugin</name>\n <description>Ant plugin for HuntBugs Java bytecode static analysis tool</description>\n\n <dependencies>\n  <dependency>\n   <groupId>one.util</groupId>\n   <artifactId>huntbugs</artifactId>\n   <version>${project.version}</version>\n  </dependency>\n  <dependency>\n   <groupId>org.apache.ant</groupId>\n   <artifactId>ant</artifactId>\n   <version>1.7.1</version>\n   <scope>provided</scope>\n  </dependency>\n </dependencies>\n\n <build>\n  <plugins>\n   <plugin>\n    <groupId>org.apache.maven.plugins</groupId>\n    <artifactId>maven-jar-plugin</artifactId>\n    <version>2.4</version>\n   </plugin>\n   <plugin>\n    <groupId>org.apache.maven.plugins</groupId>\n    <artifactId>maven-shade-plugin</artifactId>\n    <version>2.4.3</version>\n    <executions>\n     <execution>\n      <phase>package</phase>\n      <goals>\n       <goal>shade</goal>\n      </goals>\n      <configuration>\n       <shadedArtifactAttached>true</shadedArtifactAttached>\n       <shadedClassifierName>nodeps</shadedClassifierName>\n      </configuration>\n     </execution>\n    </executions>\n   </plugin>\n  </plugins>\n </build>\n\n <scm>\n  <connection>scm:git:https://github.com/amaembo/huntbugs.git</connection>\n  <developerConnection>scm:git:https://github.com/amaembo/huntbugs.git</developerConnection>\n  <url>https://github.com/amaembo/huntbugs.git</url>\n   <tag>HEAD</tag>\n  </scm>\n</project>\n"
  },
  {
    "path": "huntbugs-ant-plugin/src/main/java/one/util/huntbugs/ant/HuntBugsTask.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.ant;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.jar.JarFile;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport one.util.huntbugs.analysis.AnalysisOptions;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.HuntBugsResult;\nimport one.util.huntbugs.input.XmlReportReader;\nimport one.util.huntbugs.output.Reports;\nimport one.util.huntbugs.repo.AuxRepository;\nimport one.util.huntbugs.repo.CompositeRepository;\nimport one.util.huntbugs.repo.DirRepository;\nimport one.util.huntbugs.repo.JarRepository;\nimport one.util.huntbugs.repo.Repository;\n\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\nimport org.apache.tools.ant.types.Path;\nimport org.xml.sax.SAXException;\n\nimport com.strobel.assembler.metadata.ClasspathTypeLoader;\nimport com.strobel.assembler.metadata.CompositeTypeLoader;\nimport com.strobel.assembler.metadata.ITypeLoader;\nimport com.strobel.assembler.metadata.JarTypeLoader;\nimport com.strobel.assembler.metadata.signatures.Reifier;\n\npublic class HuntBugsTask extends Task {\n\tpublic enum LogLevel {\n\t\tQUIET, VERBOSE;\n\t}\n\t\n\tprivate Path classPath;\n\t\n\tprivate Path auxClassPath;\n\t\n\tprivate File xml;\n\n\tprivate File html;\n\t\n\tprivate File diff;\n\t\n\tprivate LogLevel log = LogLevel.VERBOSE; \n\t\n\t@Override\n\tpublic void execute() throws BuildException {\n\t\t// Disable unwanted Procyon logging \n\t\tLogger.getLogger(Reifier.class.getSimpleName()).setLevel(Level.OFF);\n\t\t\n\t\tList<Repository> repos = createRepository();\n\t\tif(xml == null && html == null) {\n\t\t\tthrow new BuildException(\"Either xml or html must be specified\");\n\t\t}\n\t\tRepository repo = new CompositeRepository(repos);\n\t\tAnalysisOptions opt = new AnalysisOptions();\n\t\tContext ctx = new Context(repo, opt);\n\t\tif(log == LogLevel.VERBOSE)\n\t\t\taddListener(ctx);\n\t\tctx.analyzePackage(\"\");\n\t\tHuntBugsResult result = ctx;\n\t\tif(diff != null) {\n\t\t\ttry {\n\t\t\t\tresult = Reports.diff(XmlReportReader.read(ctx, diff.toPath()), ctx);\n\t\t\t} catch (IOException | SAXException | ParserConfigurationException e) {\n\t\t\t\tSystem.err.println(\"Unable to read old report \"+diff+\": \"+e);\n\t\t\t\tSystem.err.println(\"Skipping diff generation\");\n\t\t\t}\n\t\t}\n\t\tReports.write(xml == null ? null : xml.toPath(), html == null ? null\n\t\t\t\t: html.toPath(), result);\n\t}\n\n\tprivate void addListener(Context ctx) {\n\t\tlong[] lastPrint = {0};\n        ctx.addListener((stepName, className, count, total) -> {\n            if (count == total || System.currentTimeMillis() - lastPrint[0] > 2000) {\n                System.err.println(\"HuntBugs: \" + stepName + \" [\" + count + \"/\" + total + \"]\");\n                lastPrint[0] = System.currentTimeMillis();\n            }\n            return true;\n        });\n\t}\n\n\tprivate List<Repository> createRepository() {\n\t\tif(classPath == null || classPath.size() == 0) {\n\t\t\tthrow new BuildException(\"Please specify classPath!\");\n\t\t}\n\t\tList<Repository> repos = new ArrayList<>();\n\t\tfor(String path : classPath.list()) {\n\t\t\tFile file = new File(path);\n\t\t\tif(file.isDirectory()) {\n\t\t\t\trepos.add(new DirRepository(file.toPath()));\n\t\t\t} else if(file.isFile()) {\n\t\t\t\ttry {\n\t\t\t\t\trepos.add(new JarRepository(new JarFile(file)));\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tthrow new BuildException(e);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new BuildException(\"Class path element not found: \"+path);\n\t\t\t}\n\t\t}\n\t\tif(auxClassPath != null) {\n\t\t\tList<ITypeLoader> auxLoaders = new ArrayList<>();\n\t\t\tfor(String path : auxClassPath.list()) {\n\t\t\t\tFile file = new File(path);\n\t\t\t\tif(file.isDirectory()) {\n\t\t\t\t\tauxLoaders.add(new ClasspathTypeLoader(file.toString()));\n\t\t\t\t} else if(file.isFile()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tauxLoaders.add(new JarTypeLoader(new JarFile(file)));\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\tthrow new BuildException(e);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow new BuildException(\"Aux class path element not found: \"+path);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(!auxLoaders.isEmpty()) {\n\t\t\t\trepos.add(new AuxRepository(new CompositeTypeLoader(auxLoaders.toArray(new ITypeLoader[0]))));\n\t\t\t}\n\t\t}\n\t\treturn repos;\n\t}\n\n\tpublic void setClassPath(Path classPath) {\n\t\tif(this.classPath == null)\n\t\t\tthis.classPath = new Path(getProject());\n\t\tthis.classPath.append(classPath);\n\t}\n\n\tpublic void setAuxClassPath(Path auxClassPath) {\n\t\tif(this.auxClassPath == null)\n\t\t\tthis.auxClassPath = new Path(getProject());\n\t\tthis.auxClassPath.append(auxClassPath);\n\t}\n\t\n\tpublic void setDiff(File diff) {\n\t\tthis.diff = diff;\n\t}\n\n\tpublic void setXml(File xml) {\n\t\tthis.xml = xml;\n\t}\n\n\tpublic void setHtml(File html) {\n\t\tthis.html = html;\n\t}\n\t\n\tpublic void setLog(LogLevel log) {\n\t\tthis.log = log;\n\t}\n}\n"
  },
  {
    "path": "huntbugs-ant-plugin/src/main/resources/one/util/huntbugs/ant/antlib.xml",
    "content": "<?xml version=\"1.0\"?>\n<antlib>\n   <taskdef name=\"huntbugs\" classname=\"one.util.huntbugs.ant.HuntBugsTask\"/>\n</antlib>"
  },
  {
    "path": "huntbugs-maven-plugin/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "huntbugs-maven-plugin/.gitignore",
    "content": "/target/\n"
  },
  {
    "path": "huntbugs-maven-plugin/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>huntbugs-maven-plugin</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "huntbugs-maven-plugin/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/main/resources=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "huntbugs-maven-plugin/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.assertIdentifier=error\norg.eclipse.jdt.core.compiler.problem.enumIdentifier=error\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\norg.eclipse.jdt.core.formatter.align_type_members_on_columns=false\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=20\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_assignment=0\norg.eclipse.jdt.core.formatter.alignment_for_binary_expression=20\norg.eclipse.jdt.core.formatter.alignment_for_compact_if=16\norg.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80\norg.eclipse.jdt.core.formatter.alignment_for_enum_constants=0\norg.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16\norg.eclipse.jdt.core.formatter.alignment_for_method_declaration=0\norg.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16\norg.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80\norg.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16\norg.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16\norg.eclipse.jdt.core.formatter.blank_lines_after_imports=1\norg.eclipse.jdt.core.formatter.blank_lines_after_package=1\norg.eclipse.jdt.core.formatter.blank_lines_before_field=0\norg.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0\norg.eclipse.jdt.core.formatter.blank_lines_before_imports=1\norg.eclipse.jdt.core.formatter.blank_lines_before_member_type=1\norg.eclipse.jdt.core.formatter.blank_lines_before_method=1\norg.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1\norg.eclipse.jdt.core.formatter.blank_lines_before_package=0\norg.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1\norg.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1\norg.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false\norg.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false\norg.eclipse.jdt.core.formatter.comment.format_block_comments=true\norg.eclipse.jdt.core.formatter.comment.format_header=false\norg.eclipse.jdt.core.formatter.comment.format_html=true\norg.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true\norg.eclipse.jdt.core.formatter.comment.format_line_comments=true\norg.eclipse.jdt.core.formatter.comment.format_source_code=false\norg.eclipse.jdt.core.formatter.comment.indent_parameter_description=false\norg.eclipse.jdt.core.formatter.comment.indent_root_tags=true\norg.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert\norg.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert\norg.eclipse.jdt.core.formatter.comment.line_length=80\norg.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true\norg.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true\norg.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false\norg.eclipse.jdt.core.formatter.compact_else_if=true\norg.eclipse.jdt.core.formatter.continuation_indentation=2\norg.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2\norg.eclipse.jdt.core.formatter.disabling_tag=@formatter\\:off\norg.eclipse.jdt.core.formatter.enabling_tag=@formatter\\:on\norg.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false\norg.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true\norg.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true\norg.eclipse.jdt.core.formatter.indent_empty_lines=false\norg.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true\norg.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true\norg.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true\norg.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false\norg.eclipse.jdt.core.formatter.indentation.size=4\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert\norg.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert\norg.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert\norg.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert\norg.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert\norg.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert\norg.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert\norg.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.join_lines_in_comments=true\norg.eclipse.jdt.core.formatter.join_wrapped_lines=true\norg.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false\norg.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false\norg.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false\norg.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false\norg.eclipse.jdt.core.formatter.lineSplit=120\norg.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false\norg.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false\norg.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0\norg.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1\norg.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true\norg.eclipse.jdt.core.formatter.tabulation.char=space\norg.eclipse.jdt.core.formatter.tabulation.size=4\norg.eclipse.jdt.core.formatter.use_on_off_tags=false\norg.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false\norg.eclipse.jdt.core.formatter.wrap_before_binary_operator=true\norg.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true\norg.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=false\n"
  },
  {
    "path": "huntbugs-maven-plugin/.settings/org.eclipse.jdt.ui.prefs",
    "content": "eclipse.preferences.version=1\neditor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true\nformatter_profile=_StreamEx\nformatter_settings_version=12\norg.eclipse.jdt.ui.javadoc=true\norg.eclipse.jdt.ui.text.custom_code_templates=<?xml version\\=\"1.0\" encoding\\=\"UTF-8\" standalone\\=\"no\"?><templates><template autoinsert\\=\"true\" context\\=\"gettercomment_context\" deleted\\=\"false\" description\\=\"Comment for getter method\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.gettercomment\" name\\=\"gettercomment\">/**\\r\\n * @return the ${bare_field_name}\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"settercomment_context\" deleted\\=\"false\" description\\=\"Comment for setter method\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.settercomment\" name\\=\"settercomment\">/**\\r\\n * @param ${param} the ${bare_field_name} to set\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"constructorcomment_context\" deleted\\=\"false\" description\\=\"Comment for created constructors\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.constructorcomment\" name\\=\"constructorcomment\">/**\\r\\n * ${tags}\\r\\n */</template><template autoinsert\\=\"false\" context\\=\"filecomment_context\" deleted\\=\"false\" description\\=\"Comment for created Java files\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.filecomment\" name\\=\"filecomment\">/*\\r\\n * Copyright 2016 HuntBugs contributors\\r\\n * \\r\\n * Licensed under the Apache License, Version 2.0 (the \"License\");\\r\\n * you may not use this file except in compliance with the License.\\r\\n * You may obtain a copy of the License at\\r\\n * \\r\\n *     http\\://www.apache.org/licenses/LICENSE-2.0\\r\\n * \\r\\n * Unless required by applicable law or agreed to in writing, software\\r\\n * distributed under the License is distributed on an \"AS IS\" BASIS,\\r\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\r\\n * See the License for the specific language governing permissions and\\r\\n * limitations under the License.\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"typecomment_context\" deleted\\=\"false\" description\\=\"Comment for created types\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.typecomment\" name\\=\"typecomment\">/**\\r\\n * @author ${user}\\r\\n *\\r\\n * ${tags}\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"fieldcomment_context\" deleted\\=\"false\" description\\=\"Comment for fields\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.fieldcomment\" name\\=\"fieldcomment\">/**\\r\\n * \\r\\n */</template><template autoinsert\\=\"true\" context\\=\"methodcomment_context\" deleted\\=\"false\" description\\=\"Comment for non-overriding methods\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.methodcomment\" name\\=\"methodcomment\">/**\\r\\n * ${tags}\\r\\n */</template><template autoinsert\\=\"false\" context\\=\"overridecomment_context\" deleted\\=\"false\" description\\=\"Comment for overriding methods\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.overridecomment\" name\\=\"overridecomment\"/><template autoinsert\\=\"true\" context\\=\"delegatecomment_context\" deleted\\=\"false\" description\\=\"Comment for delegate methods\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.delegatecomment\" name\\=\"delegatecomment\">/**\\r\\n * ${tags}\\r\\n * ${see_to_target}\\r\\n */</template><template autoinsert\\=\"true\" context\\=\"newtype_context\" deleted\\=\"false\" description\\=\"Newly created files\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.newtype\" name\\=\"newtype\">${filecomment}\\r\\n${package_declaration}\\r\\n\\r\\n${typecomment}\\r\\n${type_declaration}</template><template autoinsert\\=\"true\" context\\=\"classbody_context\" deleted\\=\"false\" description\\=\"Code in new class type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.classbody\" name\\=\"classbody\">\\r\\n</template><template autoinsert\\=\"true\" context\\=\"interfacebody_context\" deleted\\=\"false\" description\\=\"Code in new interface type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.interfacebody\" name\\=\"interfacebody\">\\r\\n</template><template autoinsert\\=\"true\" context\\=\"enumbody_context\" deleted\\=\"false\" description\\=\"Code in new enum type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.enumbody\" name\\=\"enumbody\">\\r\\n</template><template autoinsert\\=\"true\" context\\=\"annotationbody_context\" deleted\\=\"false\" description\\=\"Code in new annotation type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.annotationbody\" name\\=\"annotationbody\">\\r\\n</template><template autoinsert\\=\"true\" context\\=\"catchblock_context\" deleted\\=\"false\" description\\=\"Code in new catch blocks\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.catchblock\" name\\=\"catchblock\">// ${todo} Auto-generated catch block\\r\\n${exception_var}.printStackTrace();</template><template autoinsert\\=\"true\" context\\=\"methodbody_context\" deleted\\=\"false\" description\\=\"Code in created method stubs\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.methodbody\" name\\=\"methodbody\">// ${todo} Auto-generated method stub\\r\\n${body_statement}</template><template autoinsert\\=\"true\" context\\=\"constructorbody_context\" deleted\\=\"false\" description\\=\"Code in created constructor stubs\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.constructorbody\" name\\=\"constructorbody\">${body_statement}\\r\\n// ${todo} Auto-generated constructor stub</template><template autoinsert\\=\"true\" context\\=\"getterbody_context\" deleted\\=\"false\" description\\=\"Code in created getters\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.getterbody\" name\\=\"getterbody\">return ${field};</template><template autoinsert\\=\"true\" context\\=\"setterbody_context\" deleted\\=\"false\" description\\=\"Code in created setters\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.setterbody\" name\\=\"setterbody\">${field} \\= ${param};</template></templates>\nsp_cleanup.add_default_serial_version_id=true\nsp_cleanup.add_generated_serial_version_id=false\nsp_cleanup.add_missing_annotations=true\nsp_cleanup.add_missing_deprecated_annotations=true\nsp_cleanup.add_missing_methods=false\nsp_cleanup.add_missing_nls_tags=false\nsp_cleanup.add_missing_override_annotations=true\nsp_cleanup.add_missing_override_annotations_interface_methods=true\nsp_cleanup.add_serial_version_id=false\nsp_cleanup.always_use_blocks=true\nsp_cleanup.always_use_parentheses_in_expressions=false\nsp_cleanup.always_use_this_for_non_static_field_access=false\nsp_cleanup.always_use_this_for_non_static_method_access=false\nsp_cleanup.convert_functional_interfaces=false\nsp_cleanup.convert_to_enhanced_for_loop=false\nsp_cleanup.correct_indentation=false\nsp_cleanup.format_source_code=false\nsp_cleanup.format_source_code_changes_only=false\nsp_cleanup.insert_inferred_type_arguments=false\nsp_cleanup.make_local_variable_final=false\nsp_cleanup.make_parameters_final=false\nsp_cleanup.make_private_fields_final=true\nsp_cleanup.make_type_abstract_if_missing_method=false\nsp_cleanup.make_variable_declarations_final=true\nsp_cleanup.never_use_blocks=false\nsp_cleanup.never_use_parentheses_in_expressions=true\nsp_cleanup.on_save_use_additional_actions=true\nsp_cleanup.organize_imports=false\nsp_cleanup.qualify_static_field_accesses_with_declaring_class=false\nsp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true\nsp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true\nsp_cleanup.qualify_static_member_accesses_with_declaring_class=false\nsp_cleanup.qualify_static_method_accesses_with_declaring_class=false\nsp_cleanup.remove_private_constructors=true\nsp_cleanup.remove_redundant_type_arguments=true\nsp_cleanup.remove_trailing_whitespaces=false\nsp_cleanup.remove_trailing_whitespaces_all=true\nsp_cleanup.remove_trailing_whitespaces_ignore_empty=false\nsp_cleanup.remove_unnecessary_casts=true\nsp_cleanup.remove_unnecessary_nls_tags=false\nsp_cleanup.remove_unused_imports=true\nsp_cleanup.remove_unused_local_variables=false\nsp_cleanup.remove_unused_private_fields=true\nsp_cleanup.remove_unused_private_members=false\nsp_cleanup.remove_unused_private_methods=true\nsp_cleanup.remove_unused_private_types=true\nsp_cleanup.sort_members=false\nsp_cleanup.sort_members_all=false\nsp_cleanup.use_anonymous_class_creation=false\nsp_cleanup.use_blocks=false\nsp_cleanup.use_blocks_only_for_return_and_throw=false\nsp_cleanup.use_lambda=true\nsp_cleanup.use_parentheses_in_expressions=false\nsp_cleanup.use_this_for_non_static_field_access=false\nsp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true\nsp_cleanup.use_this_for_non_static_method_access=false\nsp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true\nsp_cleanup.use_type_arguments=false\n"
  },
  {
    "path": "huntbugs-maven-plugin/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "huntbugs-maven-plugin/.settings/org.eclipse.pde.core.prefs",
    "content": "BUNDLE_ROOT_PATH=target/classes\neclipse.preferences.version=1\n"
  },
  {
    "path": "huntbugs-maven-plugin/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n   <groupId>one.util</groupId>\n   <artifactId>huntbugs-all</artifactId>\n   <version>0.0.12-SNAPSHOT</version>\n  </parent>\n  <artifactId>huntbugs-maven-plugin</artifactId>\n  <packaging>maven-plugin</packaging>\n\n  <name>huntbugs-maven-plugin Maven Plugin</name>\n  <description>Maven plugin for HuntBugs Java bytecode static analysis tool</description>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.apache.maven</groupId>\n      <artifactId>maven-plugin-api</artifactId>\n      <version>2.0</version>\n    </dependency>\n    <dependency>\n      <groupId>org.apache.maven</groupId>\n      <artifactId>maven-core</artifactId>\n      <version>3.2.1</version>\n    </dependency>\n    <dependency>\n      <groupId>org.apache.maven.shared</groupId>\n      <artifactId>maven-dependency-tree</artifactId>\n      <version>2.1</version>\n    </dependency>\n    <dependency>\n      <groupId>org.apache.maven.plugin-tools</groupId>\n      <artifactId>maven-plugin-annotations</artifactId>\n      <version>3.2</version>\n      <scope>provided</scope>\n    </dependency>\n    <dependency>\n      <groupId>org.codehaus.plexus</groupId>\n      <artifactId>plexus-utils</artifactId>\n      <version>3.0.8</version>\n    </dependency>\n    <dependency>\n      <groupId>one.util</groupId>\n      <artifactId>huntbugs</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>4.12</version>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-plugin-plugin</artifactId>\n        <version>3.3</version>\n        <configuration>\n          <goalPrefix>huntbugs</goalPrefix>\n          <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>\n        </configuration>\n        <executions>\n          <execution>\n            <id>mojo-descriptor</id>\n            <goals>\n              <goal>descriptor</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>help-goal</id>\n            <goals>\n              <goal>helpmojo</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "huntbugs-maven-plugin/src/main/java/one/util/huntbugs/maven/plugin/HuntBugsMojo.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.maven.plugin;\n\nimport com.strobel.assembler.metadata.ClasspathTypeLoader;\nimport com.strobel.assembler.metadata.CompositeTypeLoader;\nimport com.strobel.assembler.metadata.ITypeLoader;\nimport com.strobel.assembler.metadata.JarTypeLoader;\nimport one.util.huntbugs.analysis.AnalysisOptions;\nimport one.util.huntbugs.analysis.Context;\nimport one.util.huntbugs.analysis.HuntBugsResult;\nimport one.util.huntbugs.input.XmlReportReader;\nimport one.util.huntbugs.output.Reports;\nimport one.util.huntbugs.repo.AuxRepository;\nimport one.util.huntbugs.repo.CompositeRepository;\nimport one.util.huntbugs.repo.DirRepository;\nimport one.util.huntbugs.repo.Repository;\nimport one.util.huntbugs.warning.Warning;\nimport org.apache.maven.artifact.Artifact;\nimport org.apache.maven.artifact.repository.ArtifactRepository;\nimport org.apache.maven.artifact.resolver.filter.ArtifactFilter;\nimport org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;\nimport org.apache.maven.execution.MavenSession;\nimport org.apache.maven.plugin.AbstractMojo;\nimport org.apache.maven.plugin.MojoExecutionException;\nimport org.apache.maven.plugin.MojoFailureException;\nimport org.apache.maven.plugins.annotations.Component;\nimport org.apache.maven.plugins.annotations.LifecyclePhase;\nimport org.apache.maven.plugins.annotations.Mojo;\nimport org.apache.maven.plugins.annotations.Parameter;\nimport org.apache.maven.project.MavenProject;\nimport org.apache.maven.shared.dependency.tree.DependencyNode;\nimport org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;\nimport org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;\nimport org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.jar.JarFile;\n\n/**\n * Goal which launches the HuntBugs static analyzer tool.\n */\n@Mojo(name = \"huntbugs\", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, requiresProject = true, threadSafe = true)\npublic class HuntBugsMojo extends AbstractMojo {\n    /**\n     * Location of the file.\n     */\n    @Parameter(defaultValue = \"${project.build.directory}/huntbugs\", property = \"outputDir\", required = true)\n    private File outputDirectory;\n\n    /**\n     * Location of classes to analyze\n     */\n    @Parameter(defaultValue = \"${project.build.outputDirectory}\", property = \"classesDir\", required = true)\n    private File classesDirectory;\n\n    /**\n     * Minimal warning score to report\n     */\n    @Parameter(defaultValue = \"30\", property = \"minScore\", required = true)\n    private int minScore;\n    \n    /**\n     * Score to fail build\n     */\n    @Parameter(defaultValue = \"0\", property = \"failScore\", required = false)\n    private int failScore;\n    \n    /**\n     * Do not print progress messages\n     */\n    @Parameter(defaultValue = \"false\", property = \"quiet\", required = true)\n    private boolean quiet;\n    \n    /**\n     * If true and report already exists, generate diff report with previous version\n     */\n    @Parameter(defaultValue = \"true\", property = \"diff\", required = true)\n    private boolean diff;\n    \n    @Parameter( defaultValue = \"${project.compileClasspathElements}\", readonly = true, required = true )\n    private List<String> classpathElements;\n    \n    @Parameter( defaultValue = \"${project}\", readonly = true, required = true )\n    private MavenProject project;\n\n    @Parameter( defaultValue = \"${session}\", readonly = true, required = true )\n    private MavenSession session;\n\n    @Parameter(property = \"skip\", defaultValue = \"false\")\n    private boolean skip;\n    \n    @Component\n    private DependencyTreeBuilder treeBuilder;\n\n    @Override\n    public void execute() throws MojoExecutionException {\n        if (!skip) {\n            try {\n                Context ctx = new Context(constructRepository(), constructOptions());\n\n                if (!quiet) {\n                    addAnalysisProgressListener(ctx);\n                }\n\n                ctx.analyzePackage(\"\");\n                writeReports(ctx);\n            } catch (Exception e) {\n                throw new MojoExecutionException(\"Failed to run HuntBugs\", e);\n            }\n        }\n    }\n    \n    private Repository constructRepository() throws IOException {\n        Repository repo = new DirRepository(classesDirectory.toPath());\n        \n        if (!quiet) {\n            getLog().info(\"HuntBugs: +dir \" + classesDirectory);\n        }\n\n        // collecting project dependencies including pom and transitive dependencies\n        ArtifactFilter artifactFilter = new ScopeArtifactFilter(\"compile\");\n        DependencyNode rootNode;\n        try {\n            rootNode = treeBuilder.buildDependencyTree(project, session.getLocalRepository(), artifactFilter);\n        } catch (DependencyTreeBuilderException e) {\n            throw new RuntimeException(e);\n        }\n        CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor();\n        rootNode.accept(visitor);\n\n        // converting dependencies to type loaders\n        List<DependencyNode> nodes = visitor.getNodes();\n        List<ITypeLoader> deps = new ArrayList<>();\n        for (DependencyNode dependencyNode : nodes) {\n            int state = dependencyNode.getState();\n\n            // checking that transitive dependency is NOT excluded\n            if (state == DependencyNode.INCLUDED) {\n                Artifact artifact = dependencyNode.getArtifact();\n                addDependency(artifact, deps);\n            }\n        }\n        \n        if (deps.isEmpty()) {\n            return repo;\n        }\n        \n        return new CompositeRepository(\n            Arrays.asList(repo, new AuxRepository(new CompositeTypeLoader(deps.toArray(new ITypeLoader[0])))));\n    }\n\n    private void addDependency(Artifact art, List<ITypeLoader> deps) throws IOException {\n        if (\"compile\".equals(art.getScope())) {\n            ArtifactRepository localRepository = session.getLocalRepository();\n            File f = localRepository.find(art).getFile();\n            if (f != null) {\n                Path path = f.toPath();\n                if (!quiet) {\n                    getLog().info(\"HuntBugs: +dep \" + path);\n                }\n                if (Files.isRegularFile(path) && art.getType().equals(\"jar\")) {\n                    deps.add(new JarTypeLoader(new JarFile(path.toFile())));\n                } else if (Files.isDirectory(path)) {\n                    deps.add(new ClasspathTypeLoader(path.toString()));\n                }\n            }\n        }\n    }\n\n    private AnalysisOptions constructOptions() {\n        AnalysisOptions options = new AnalysisOptions();\n        options.minScore = minScore;\n        \n        return options;\n    }\n    \n    private void addAnalysisProgressListener(Context ctx) {\n        long[] lastPrint = {0};\n        ctx.addListener((stepName, className, count, total) -> {\n            if (count == total || System.currentTimeMillis() - lastPrint[0] > 2000) {\n                getLog().info(\"HuntBugs: \" + stepName + \" [\" + count + \"/\" + total + \"]\");\n                lastPrint[0] = System.currentTimeMillis();\n            }\n            return true;\n        });\n    }\n    \n    private void writeReports(Context ctx) throws Exception {\n        getLog().info(\"HuntBugs: Writing report (\" + ctx.getStat(\"Warnings\") + \" warnings)\");\n        Path path = outputDirectory.toPath();\n        Files.createDirectories(path);\n        Path xmlFile = path.resolve(\"report.xml\");\n        Path htmlFile = path.resolve(\"report.html\");\n        HuntBugsResult res = ctx;\n        if(diff && Files.isRegularFile(xmlFile)) {\n            res = Reports.diff(XmlReportReader.read(ctx, xmlFile), ctx);\n        }\n        Reports.write(xmlFile, htmlFile, res);\n        if (failScore > 0 && res.warnings().mapToInt(Warning::getScore).anyMatch(score -> score >= failScore)) {\n            throw new MojoFailureException(\"There are warnings with score higher than \" + failScore);\n        }\n    }\n}\n"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n\n <groupId>one.util</groupId>\n <artifactId>huntbugs-all</artifactId>\n <version>0.0.12-SNAPSHOT</version>\n <packaging>pom</packaging>\n\n <name>huntbugs-all</name>\n <description>Java bytecode static analysis tool</description>\n <url>https://github.com/amaembo/huntbugs</url>\n <licenses>\n  <license>\n   <name>Apache License, Version 2.0</name>\n   <url>https://www.apache.org/licenses/LICENSE-2.0</url>\n   <distribution>repo</distribution>\n  </license>\n </licenses>\n\n <modules>\n  <module>huntbugs</module>\n  <module>huntbugs-ant-plugin</module>\n  <module>huntbugs-maven-plugin</module>\n  <module>sample-huntbugs-custom-detector</module>\n  <module>sample-project</module>\n </modules>\n\n <distributionManagement>\n  <snapshotRepository>\n   <id>ossrh</id>\n   <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n  </snapshotRepository>\n </distributionManagement>\n\n <developers>\n  <developer>\n   <name>Tagir Valeev</name>\n   <email>lany@ngs.ru</email>\n  </developer>\n </developers>\n\n <properties>\n  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n </properties>\n\n <scm>\n  <connection>scm:git:https://github.com/amaembo/huntbugs.git</connection>\n  <developerConnection>scm:git:https://github.com/amaembo/huntbugs.git</developerConnection>\n  <url>https://github.com/amaembo/huntbugs.git</url>\n   <tag>HEAD</tag>\n  </scm>\n\n <build>\n  <plugins>\n   <plugin>\n    <groupId>org.apache.maven.plugins</groupId>\n    <artifactId>maven-compiler-plugin</artifactId>\n    <version>3.7.0</version>\n    <configuration>\n     <source>1.8</source>\n     <target>1.8</target>\n     <compilerArgs>\n      <arg>-Xlint:all</arg>\n     </compilerArgs>\n    </configuration>\n   </plugin>\n   <plugin>\n    <groupId>org.apache.maven.plugins</groupId>\n    <artifactId>maven-source-plugin</artifactId>\n    <version>3.0.1</version>\n    <executions>\n     <execution>\n      <id>attach-sources</id>\n      <goals>\n       <goal>jar-no-fork</goal>\n      </goals>\n     </execution>\n    </executions>\n   </plugin>\n   <plugin>\n    <groupId>org.sonatype.plugins</groupId>\n    <artifactId>nexus-staging-maven-plugin</artifactId>\n    <version>1.6.6</version>\n    <extensions>true</extensions>\n    <configuration>\n     <serverId>ossrh</serverId>\n     <nexusUrl>https://oss.sonatype.org/</nexusUrl>\n     <autoReleaseAfterClose>true</autoReleaseAfterClose>\n    </configuration>\n   </plugin>\n   <plugin>\n    <groupId>org.apache.maven.plugins</groupId>\n    <artifactId>maven-javadoc-plugin</artifactId>\n    <version>2.10.4</version>\n    <executions>\n     <execution>\n      <id>attach-javadocs</id>\n      <goals>\n       <goal>jar</goal>\n      </goals>\n      <configuration>\n       <quiet>true</quiet>\n       <additionalparam>-Xdoclint:none</additionalparam>\n      </configuration>\n     </execution>\n    </executions>\n   </plugin>\n  </plugins>\n </build>\n\n <profiles>\n  <profile>\n   <id>release-sign-artifacts</id>\n   <activation>\n    <property>\n     <name>performRelease</name>\n     <value>true</value>\n    </property>\n   </activation>\n   <build>\n    <plugins>\n     <plugin>\n      <groupId>org.apache.maven.plugins</groupId>\n      <artifactId>maven-gpg-plugin</artifactId>\n      <version>1.5</version>\n      <executions>\n       <execution>\n        <id>sign-artifacts</id>\n        <phase>verify</phase>\n        <goals>\n         <goal>sign</goal>\n        </goals>\n       </execution>\n      </executions>\n     </plugin>\n    </plugins>\n   </build>\n  </profile>\n </profiles>\n</project>\n"
  },
  {
    "path": "sample-huntbugs-custom-detector/.gitignore",
    "content": "/target\n"
  },
  {
    "path": "sample-huntbugs-custom-detector/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n\n <parent>\n  <groupId>one.util</groupId>\n  <artifactId>huntbugs-all</artifactId>\n  <version>0.0.12-SNAPSHOT</version>\n </parent>\n <artifactId>sample-huntbugs-custom-detector</artifactId>\n <packaging>jar</packaging>\n\n <name>sample-huntbugs-custom-detector</name>\n <description>Demonstration of how HuntBugs custom detectors could be implemented</description>\n\n <dependencies>\n  <dependency>\n   <groupId>one.util</groupId>\n    <artifactId>huntbugs</artifactId>\n    <version>${project.version}</version>\n    <scope>provided</scope>\n   </dependency>\n   <dependency>\n    <groupId>junit</groupId>\n    <artifactId>junit</artifactId>\n    <version>4.12</version>\n    <scope>test</scope>\n   </dependency>\n </dependencies>\n\n</project>\n"
  },
  {
    "path": "sample-huntbugs-custom-detector/src/main/java/one/util/huntbugs/sample/SampleHuntBugsPlugin.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.sample;\n\nimport one.util.huntbugs.spi.HuntBugsPlugin;\n\n/**\n * @author Mihails Volkovs\n */\npublic class SampleHuntBugsPlugin implements HuntBugsPlugin {\n\n    @Override\n    public String name() {\n        return \"Sample Detectors\";\n    }\n\n    @Override\n    public String detectorPackage() {\n        return \"one.util.huntbugs.sample.detect\";\n    }\n\n}\n"
  },
  {
    "path": "sample-huntbugs-custom-detector/src/main/java/one/util/huntbugs/sample/detect/SampleCustomDetector.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.sample.detect;\n\nimport com.strobel.assembler.metadata.MethodDefinition;\nimport com.strobel.assembler.metadata.TypeDefinition;\nimport com.strobel.assembler.metadata.TypeReference;\nimport one.util.huntbugs.registry.MethodContext;\nimport one.util.huntbugs.registry.anno.MethodVisitor;\nimport one.util.huntbugs.registry.anno.WarningDefinition;\nimport one.util.huntbugs.util.Types;\n\n/**\n * @author Mihails Volkovs\n */\n@WarningDefinition(category=\"Demo\", name=\"SampleCustomDetector\", maxScore=80)\npublic class SampleCustomDetector {\n\n    @MethodVisitor\n    public void visit(MethodContext mc, MethodDefinition md, TypeDefinition td) {\n        TypeReference returnType = md.getReturnType();\n        if (Types.isCollection(returnType)) {\n            mc.report(\"SampleCustomDetector\", 5);\n        }\n    }\n}\n"
  },
  {
    "path": "sample-huntbugs-custom-detector/src/main/resources/META-INF/services/one.util.huntbugs.spi.HuntBugsPlugin",
    "content": "one.util.huntbugs.sample.SampleHuntBugsPlugin\n"
  },
  {
    "path": "sample-huntbugs-custom-detector/src/main/resources/huntbugs/messages.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<p:Messages\n  xmlns:p=\"https://raw.githubusercontent.com/amaembo/huntbugs/master/huntbugs/src/main/resources/huntbugs\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"https://raw.githubusercontent.com/amaembo/huntbugs/master/huntbugs/src/main/resources/huntbugs messages.xsd\">\n  <WarningList>\n    <Warning Type=\"SampleCustomDetector\">\n      <Title>For some reason you can't return instance of java.util.Collection</Title>\n      <Description>For some reason you can't return instance of java.util.Collection</Description>\n      <LongDescription><![CDATA[For some reason you can't return instance of java.util.Collection]]></LongDescription>\n    </Warning>\n  </WarningList>\n</p:Messages>\n"
  },
  {
    "path": "sample-huntbugs-custom-detector/src/test/java/one/util/huntbugs/sample/DataTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.sample;\n\nimport one.util.huntbugs.spi.DataTests;\nimport org.junit.Test;\n\n/**\n * @author Mihails Volkovs\n */\npublic class DataTest {\n\n    @Test\n    public void test() throws Exception {\n        DataTests.test(\"one/util/huntbugs/sample/testdata\");\n    }\n\n}\n"
  },
  {
    "path": "sample-huntbugs-custom-detector/src/test/java/one/util/huntbugs/sample/testdata/TestSampleCustomDetector.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.sample.testdata;\n\nimport one.util.huntbugs.registry.anno.AssertNoWarning;\nimport one.util.huntbugs.registry.anno.AssertWarning;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Mihails Volkovs\n */\npublic class TestSampleCustomDetector {\n\n    private static final String DEMO_CUSTOM_DETECTOR = \"SampleCustomDetector\";\n\n    @AssertWarning(DEMO_CUSTOM_DETECTOR)\n    public Collection getCollection() {\n        return new ArrayList();\n    }\n\n    @AssertNoWarning(DEMO_CUSTOM_DETECTOR)\n    public Map getMap() {\n        return new HashMap();\n    }\n\n}\n"
  },
  {
    "path": "sample-project/.gitignore",
    "content": "/target\n"
  },
  {
    "path": "sample-project/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>one.util</groupId>\n        <artifactId>huntbugs-all</artifactId>\n        <version>0.0.12-SNAPSHOT</version>\n    </parent>\n    <artifactId>sample-project</artifactId>\n    <packaging>jar</packaging>\n\n    <name>sample-project</name>\n    <description>Sample project to demonstrate HuntBugs configuration and features</description>\n\n    <dependencies>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.12</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>one.util</groupId>\n                <artifactId>huntbugs-maven-plugin</artifactId>\n                <version>${project.version}</version>\n                <dependencies>\n                    <dependency>\n                        <groupId>one.util</groupId>\n                        <artifactId>sample-huntbugs-custom-detector</artifactId>\n                        <version>${project.version}</version>\n                    </dependency>\n                </dependencies>\n                <executions>\n                    <execution>\n                        <id>huntbugs-check</id>\n                        <phase>prepare-package</phase>\n                        <goals>\n                            <goal>huntbugs</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "sample-project/src/main/java/one/util/huntbugs/sample/MyProductionCode.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.sample;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Mihails Volkovs\n */\npublic class MyProductionCode {\n\n    public List<String> bugsAlive() {\n        return new ArrayList<>();\n    }\n\n}\n"
  },
  {
    "path": "sample-project/src/test/java/one/util/huntbugs/sample/MyProductionCodeTest.java",
    "content": "/*\n * Copyright 2016 HuntBugs contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage one.util.huntbugs.sample;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author Mihails Volkovs\n */\npublic class MyProductionCodeTest {\n\n    private MyProductionCode productionCode;\n\n    @Before\n    public void setUp() {\n        productionCode = new MyProductionCode();\n    }\n\n    @Test\n    public void bugsAlive() {\n        assertTrue(productionCode.bugsAlive().isEmpty());\n    }\n\n}"
  }
]