From 767536605379e492929a763f4a585cb4f499b9f6 Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Tue, 15 Oct 2013 19:49:27 -0700 Subject: Snapshot 246ebf4786ef75849985fa5fba6015155ebac527 from idea/132.637 of git://git.jetbrains.org/idea/community.git 246ebf4: better gradient colors for Darcula 8833be9: should be opaque 9a1a2f2: fix darcula text fields 001ed5d: add "perflib" from tools-base module to build c348af4: Github: do not provide Git AuthData for wrong host 7424aca: Github: compare hosts case-insensitive 7ce90f4: fix double Shift on Windows 050b38c: check value of Required annotation; do not check required attributes of dom elements in XmlHighlightingVisitor; revive testing of required attribute validation in PluginXmlFunctionalTest 59d7274: IDEA-114873 (Add Package Info item to New submenu) c4d7c3e: codestyle fix 9f3e60a: trace about substitutor once per derived class, in order to avoid excessive logging 27952b2: moved FinderRecursivePanel to platform-impl 94b9f06: IDEA-114850 Unreadable "No files are open" text 1fd5b2d: removing nested frameworks 4098566: capitalization (IDEA-81968) 10f8420: diagnose incorrect MixinEP registration (IDEA-113506) 35689ca: add apiVersion parameter to build.xml so that Android Studio can use exact platform build they're compatible with (IDEA-114596) cb53ae1: Multiline support for regexp editor ddef70e: Lens mode (hint location fixes) f8d6bf8: disable escaper for balloons 99d486d: check for language 26fa119: support for java regexp modifiers 925000e: Ability to handle regexps with modifiers 67f1c14: automake is run only for the project where current user activity is going on (IDEA-104064) d5cad13: fix naturalCompare() edge case c1f7724: Merge remote-tracking branch 'origin/master' 862fb67: proper bounds for search everywhere button d21dc3f: new shortcut for search everywhere 4283182: completely new UI 50e5c52: colors for white theme 08f4c9e: don't paint parent's background 0ed4058: active icon for Search Everywhere 17a6674: don't reset row height 52f84a7: IDEA-114694 Shift-Space is a terrible default shortcut for Search Everywhere 099ead0: dfa: exception handling reworked no more gosub-return causing equal states differing by offset stack only don't visit outer finally without visiting inner one 3744d2c: IDEA-114736 Bug in code completion inside instanceof block 377acad: correctly initialize semaphore for all BasicFuture implementations 81bfb1e: Lens mode (mouse wheel support & many fixes) 81d90a7: Get rid of extra UISettings notifications 2fb0092: Github: fix UrlToHost function 75f4e1f3: plugin advertisement: show disabled plugin name 8ed1b68: plugins advertisement: enabled installed plugin 53ad76d: IDEA-114708 Wrong Up/Down action interpretation in consoles (irb/Bash) e818b6e: use fileEditor's editor instead of some random editor from dataContext bd152a4: IDEA-89857 automatically adds required attributes in dom-based xml; insert correct endings for View/ViewGroup tags 7988d09: test for not building stubs for java files in module but not under source roots, in library but not in class root 131353b: IDEA-112134 $SELECTION$ variable doesn't work as true variable, but as $END$ cac9208f: item 1 of IDEA-95649 (Darcula: Settings -> File Templates doesn't follow theme colors) ab2d2ca: remove color to display file template descriptions nicely under Darcula 7fb7911: use correct company name in version info (IDEA-82623) 5bd4249: WEB-8027 long single line values in debugger hang IntelliJ + review 4710a08: show editor notification for text mapped files about known plugins from the repository 62c87af: logging for incorrect move eab3d4c: plugin advertising: stop if update was disabled 25584ad: Add MONTH_NAME_SHORT and MONTH_NAME_FULL definition & improve file template descriptions 4f10bd1: CR-IC-2706 (logging added) eef188b: Add diagnostic info of invalid template ranges fd7c9d0: EA-50616 618a573: license added? d29c66e: Drivers management refactored f280f8b: update netty (attempt to understand why nodejs disconnected on second run, but it was not netty fault) 15d9033: selecting right project type 4111497: http://ea.jetbrains.com/browser/ea_problems/50978 65eff28: EA-50605 - NPE: AbstractExternalSystemTaskConfigurationType.generateName f43865d: Slightly reworked formatter markers' UI in accordance with IDEA-114583 8e51528: give file template description a bit more room 9d2f4f9: CR-IC-2705, fixed for Mac 55b6603: fix default file template descriptions 6fd90c1: Revert optimization, because MAC tests fail. d11966f: IDEA-114766 Gradle: module dependency scope ignored while project info resolving 359ddf8: IDEA-114647 (fsnotifier size check updated) 0f10404: license added 780fa6e: allow watching exFAT (IDEA-114647) f8dc1eb: lib license added efb3281: testdata for IDEA-67591 993fb34: highlighting for incompatible return types in type parameter inheritors (IDEA-57274) e03245e: Cleanup (comment) 51a77de: platform: garbage production by FIleWatcher reduced 06b198e: Cleanup (typo; dead code) d8729d5: platform: partial refresh fixed af0637c: Merge remote-tracking branch 'origin/master' 121ee3e: Add helper method for pretty-printing JSON in TaskUtil class. Improve logging in GenericRepository. 461c6d7: testng: parameterized template (IDEA-114746) 5a50ed8: do not detach copyright updater on project closed (IDEA-114186) f06771b: restart infer nullity when library already exist (IDEA-114163) b33fa7f: effectively final: check flow at variable start (IDEA-114737) 46e9f4b: testdata for IDEA-57252 82b6b16: testdata for IDEA-67518 6a9313c: type parameter in bounds check: separate wildcards with type param in bound efc19ab: new inference: do not ignore meaningful upper bounds cbff339: Optimization: don't use FileUtil.pathsEqual() , paths returned by FileWatcher is already canonical. 2723de7: don't fold already concise getters (EA-50804) 17dae78: IDEA-114699 Live template without description shows "NULL" 56e5602: a simpler way to setup live templates in tests 3ea2e38: Jediterm updated. a47f9d3: Change cache lookup logic in XML and JSON response handlers 4ce5dec: Don't urlencode "serverUrl" template variable in GenericRepository fields 38c05d9: IDEA-114076 Unit tests can't be executed with a JRE 1.5 because junit-rt.jar is compiled with JRE 6.0 as target bb8bb2a: moved to analysis 376fe99: moved to analysis ff48d6d: moved to analysis 3fcf2d5: moved to analysis 1825351: moved to analysis 563ffda: moved to analysis dc477a1: moved to analysis 3c18086: moved to analysis 472bab7: moved to analysis 473e45d: moved to analysis 65f35d7: moved to analysis a36aaf6: moved to analysis 5f10c64: moved to analysis c3bf35d: moved to analysis b6fbbb6: moved to analysis 6a9d91b: moved to analysis 7582706: moved to analysis ef37a6c: moved to analysis 1ea667f: moved to analysis 8985a7e: moved to analysis 37691e0: moved to analysis 08ba033: moved to analysis f069df4: moved to analysis 3ac7210: moved to analysis 5e9a1e1: moved to analysis 74ecd51: moved to analysis 33f20b1: moved to analysis 936f232: read action da37289: moved to analysis e0e3724: moved to analysis 53e0050: moved to analysis 5240818: moved to analysis 50f7722: moved to analysis 8b64b6d: moved to analysis 62c27c4: moved to analysis 0dba483: moved to analysis c7bfbeb: moved to analysis 6bf16de: moved to analysis 180ea57: moved to analysis b8fcd01: moved to analysis b19c765: moved to analysis 636378c: moved to analysis 7c63c331: moved to analysis b47e90d: moved to analysis 44e0bd5: moved to analysis 4957a2c: moved to analysis bad582a: moved to analysis 2a4c6df: moved to analysis bf5ae61: moved to analysis f547ee5: moved to analysis c6d5ca6: moved to analysis c3dc3f2: moved to analysis 27f40d2: moved to analysis 82cc0c1: moved to analysis e309872: moved to analysis c6806d7: moved to analysis 66b131c: test fix 5819121: moved to analysis ace2035: moved to analysis 300fed6: moved to analysis 73d5646: moved to analysis 1efaa3b: moved to analysis 930eabe: moved to analysis 34c381d: moved to analysis 8094570: moved to analysis bb3273c: moved to analysis aec65a6: moved to analysis cbc892a: moved to analysis b42dc77: moved to analysis ba6923d: moved to analysis 2283aa0: moved to analysis 1aeb0d3: moved to analysis e08c9f1: moved to analysis 03cd373: moved to analysis caaf810: moved to analysis 0e924cf: moved to analysis f3e0d0a: moved to analysis 51ec5ea: moved to analysis 94fb66f: moved to analysis 6aaaaae: moved to analysis 08e51ae: moved to analysis cf12745: moved to analysis b34f44f: moved to analysis 37f3cdc: moved to analysis 036a2b6: moved to analysis 4931ffa: moved to analysis 8e59ca1: moved to analysis 02c0c01: moved to analysis 89a55b0: moved to analysis cba23f4: moved to analysis 1b1fb1c: moved to analysis bd725cd: moved to analysis dfcc51d: moved to analysis fad8af3: moved to analysis 827e5a5: moved to analysis 2360dcd: moved to analysis 008c56a: moved to analysis 758ba60: moved to analysis 731c642: moved to analysis d5adf28: moved to analysis b892f8b: moved to analysis 7513254: moved to analysis e3a752c: moved to analysis 03219ce: moved to analysis f06ad8a: moved to analysis 2c4614a: moved to analysis 4e076dd: moved to analysis 7f3ee23: moved to analysis 50835cc: moved to analysis ff01a67: moved to analysis 084113a: moved to analysis e197319: moved to analysis 1d7f251: moved to analysis e2fc894: moved to analysis d48b36b: EA-50870 - IAE: GroovyPropertyUtils.getGetterNameNonBoolean 0fb3c6e: IDEA-57747 (Idea X - CTRL+SHIFT+Left/Right Arrow functionality) 2cbff34: Github: use standard YesNoDialog 53782b5: Github: use strict class check a42b1d9: when launching build process or javac server suppress libraries from 'ext' directories to ensure compiler is loaded from correct location f40dfd1: OC-8512 Exception when typing on empty line at non-zero column during indexing 917a3a1: cleanup c821ba0: moved to analysis 56da4ee: moved to core 38dbce1: cleanup d8bd506: ForkJoin support 9f4a9d0: moved to analysis da9996b: moved to analysis f44d265: fix due to CR-IC-2640 comment 75b7026: Merge remote-tracking branch 'origin/master' 48e70d3: used lower level char buffer instead of document.getText() due to CR-IC-2640 comment 1271549: project structure: increased default size of source roots tree a24ebae: Merge remote-tracking branch 'origin/master' a9fc3c6: cleanup adb2f16: diagnostics for EA-50397 - assert: TextRange. 2244946: EA-50612 - NPE: EditorWindow$.run a64cbc3: Logger renamed to more specific name 2cbf40b: EA-50756 - NPE: DefaultXmlExtension.getAvailableTagNames diagnostics c25431e: new inference: skip inference for nested call when nested call was initially requested to be inferred 5fda195: detailed diagnostic for compiler class loading problems c178ebf: workaround for EA-49915, EA-50599 5e7f578: go to the errors first by default 42ad23d: when matching local variables slots and variable names from sources take into account loop and catch statements 69424d4: run test method from abstract class: ignore non-runnable inheritors; try to retrieve from context implicitly selected inheritor (IDEA-114551) 8e51c80: override: fix param names in javadoc (IDEA-114557) 977e332: PY-11027 (ignore placeholder argument from non-compliant DEs) c3af1f1: Merge remote-tracking branch 'origin/master' fa5fa39: Test skipped for Win XP bad439d: execution tests probably fixed 1e54387: range changed (IDEA-114528) 93c8631: infer nullity: more pessimistic check: check variables first (IDEA-114630) 6fb01cd: global inspections documentation cleanup 087b52a: WEB-445 "Select word at caret" a bit to grabby in class attributes 046efd8: highlight missing package-info.java in editor too b525330: Errors beef6c6: GroovyDirectInheritorsSearcher: avoid unnecessary collection->array copying 0596f3d: remove @NotNull from SortedMap#comparator (IDEA-114655) eb9828f: IDEA-114203: Add new "Options" tab in YouTrack repository editor where user can customize states names 4371063: Delete repository problem notification b8d1054: CR-IC-2693 3f3b474: CR-IC-2690 0ce5ae9: displaying version number in tree 2271eaf: IDEA-114633 (symlink deletion regression fixed) 569a70d: Cleanup (FileUtil tests grouped) 9fdcf65: Cleanup (formatting) ca49f7d: Cleanup (unnecessary condition) 46d04cb: FrameworkLibraryVersion separated from FrameworkVersion a8fc158: recognize IBM JDK from WebSphere 8 (IDEA-90661) 65f1157: Merge branch 'svn1_8_new' f165eb7: named NodeRendererImpl constructor; adjust test expectations 117f86f: corrected e173999: FrameworkGroupVersion dropped 9948d9d: Merge remote-tracking branch 'origin/master' 76f6788: fix exception on some linux machines f6f6664: IDEA-114517 Use server url (and not repository url) as authentication realm for https server certificate 5e77c10: IDEA-114517 Force certificate errors checking before credentials error checking 13c6c0a: Merge remote-tracking branch 'origin/master' 784bfa5: Merge remote-tracking branch 'origin/master' 9f25ff8: jdk8 compilation 80265c1: plugin advertiser: escape # in feature implementation efc8a3d: EA-50528 - MJE: JsonReader.syntaxError 2e10f33: IDEA-99241 (Report package naming convention violations in the editor) 1cc2fa4: made fields final 4374c69: replace exception-driven control flow with the proper one f0cba21: moved to analysis d9b6c0f: moved to psi f8c15cf: moved to psi 836e381: cleanup 2e0029d: moved to analysis 6484602: make TempFileSystem extends LocalFileSystem to simplify clients and tests 662eb80: cleanup 0ebcf72: moved to analysis 250fff4: moved to analysis 3906227: moved to analysis a24ae6d: moved to analysis e8fc8c6: moved to psi 7a468ca: moved method to core 82060fd: cleanup 04c524c: reduce dependency on LocalFileSystem 8cdab66: frameworks tree refactored 9b45d86: WEB-9563 There is a bug with Emmet and BEM namings 29ecf27: guess DTD by public id #WEB-9546 fixed c697898: JavaEE frameworks sorted out 6dc8386: less side effects 99e0eae: test is back e2d2093: Gradle: gradle-native deps added 7507dc5: do not autoscroll when in focus, e.g. selection changing action may be triggered directly from structure 346c501: add model change support & some formatting d6ff1a1: Merge remote-tracking branch 'origin/master' b6a044a: Respect auto-save and auto-sync settings. e1f4afb: IDEA-60234 Automatically unwrap groovy.lang.Reference in the debugger 0dcb367: make topic final c5ce0b8: IDEA-114604 Evaluate Expression doesn't change context after navigating call stack +review f9411d3: cleanup 89d03b1: overrides 147d5ea: cleanup aef6c9e: extracted method to avoid code duplication 4cffaf3: on rebuild clear system caches for compilers run internally in IDEA f72bc3d: add darcula green color 31d24b4: better colors for darcula 4ef7b0c: glow color for IntelliJ laf 36aad83: refactor button and checkbox 9181562: search field style for IntelliJ LaF cff6f4b: use triangles in IntelliJ LaF trees c4ebcef: add IntelliJ LaF dee1ff7: IntelliJ laf a152352: customize Darcula LaF from IntelliJ LaF 6e97b12: tree icons for IntelliJ LaF 69dbc89: inference: do not check return type during inference; separate highlighting check 9e060c7: cast 51f471a: Format pom.xml on create new module. 2624ec6: IDEA-114593 Debugger: Show variable information when no debug info 1918d6e: overrides, nullability c463545: platform: race between JNA and environment loading fixed 943cea6: Accidentally forget to add annotator to plugin.xml 0dc87d5: IDEA-50801 Evaluate expression: expand root node automatically +review 79c7ba5: overrides 5e841a7: fix testdata 7c84b97: Update JqlAnnotator to highlight JqlIdentifiers as constants, not as strings, to be consistent with YouTrack queries colors e0ac1f1: Merge remote-tracking branch 'origin/master' dfc9912: fixed PY-10964 Extract variable doesn't work as expected inside brackets fixed PY-10221 Refactor->Extract->Variable may break square bracket symmetry and may break user input 9c38ae3: Extract pretty-printing of XML responses to helper method in TaskUtil, use StringUtil.notNullize in YouTrackIntellisense models. 6c49b66: IDEA-114523 Fixed svn executable version parsing - support arbitrary text after version (in executable output) 7157489: Make myWorkspaceMap and myUnresolvedCollector volatile. 35e76f4: Comment for getNameThroughCopies method added cb377a5: spelling fixed b5eeae5: svn: Refactored RootUrlInfo - utilize underlying Node instance (instead of just copying fields) b615000: IDEA-113670 Reformat+rearrange per-language setting [CR-IC-2682] b37e22e: Merge remote-tracking branch 'origin/master' 7a40a2c: svn: @NotNull/@Nullable for NestedCopyInfo and RootUrlInfo parameters 05e6734: cleanup 60b90f9: cleanup 6e534ce: update netty (07 oct 4.1 Alpha build) Updated, but ref error was our fault, it is not netty bug. b1d9994: inference: filter out inaccessible for method ref methods 56be1b2: svn: Updated detection if svn roots were changed - added error roots tracking 265806c: svn: Simplified svn executable validation logic - removed explicit major version check 3aadab0: svn: Removed check "if working copy format of project root is compatible with command line client" (during svn executable validation) be91b71: svn: Added notification about errors during svn roots detection 7e742f4: Merge remote-tracking branch 'origin/master' 1c1e2ee: svn: Extracted showing "Subversion Working Copies Information" panel to separate method 16b2321: EA-50707 - assert: TextRange. 35546a0: new inference: ignore captured variables incorporation phase 8c0d7a1: testRunner: wording (IDEA-114500) as attached state is everywhere checked separately 9641ede: plugins notification: hide notification before dialog is shown 3ecdadc: ctors closed 199a379: framework roles 2d68bc5: platform: hidden Windows root dir with NIO2 attribute reader fixed 5ff5b2e: IDEA-113961 (ownership checking) 498d04f: IDEA-113961 (permissions in file attributes deprecated and interned) d9f1d4f: Cleanup (pointless assertion) 415b782: svn: Implemented ability to view svn roots detection errors in "Subversion Working Copies Information" panel f1ecb53: svn: Refactored RootUrlInfo - utilize Node instance on which RootUrlInfo is based b726526: svn: Implemented ability to detect svn roots when working copy format does not correspond to used client (SVNKit or executable could not perform commands on the working copy - for instance, when client is old or working copy upgrade required) 67bdd3a: svn: Implemented ability to resolve client factory and working copy format without checking detected svn roots b15784f: add some ways for reuse in DB part & cleanup 8dae62f: do not proceed with painting if placeHolder text was really painted, i.e. skip error markup, selection, etc 5795402: new inference: reject explicitly typed lambda as pertinent to applicability if its return statements are poly ae3b46c: new inference: temp: ignore inferred Object at first stage 8d2b3e3: new inference: accept methods in lambda return statement as poly without additional checks 5c40157: fixed NPE 5bf3698: Reimplement select word tests for CSS, CoffeeScript, Ruby, Python, Sass, Scss and Less a99db1d: Add test utility method for select word action testing 203e791: prefer variants without leading underscores when case sensitivity=first letter (OC-3547) 4af89f65: IDEA-114514 Grammar error in "Library already exists" dialog 215044f: Allow current element selection in StructureViewer for non-editors. 4ddd1f8: dont' copy editor markup model in language console f2ec48d: extends and implements clauses should always exist in type definitions c3ef7a1: GroovyBlockWithRange f0b2208: Merge remote-tracking branch 'origin/master' c495919: Merge remote-tracking branch 'origin/master' 7ae82a0: Save documents on switch to terminal refresh VFS on switch from it (PY-10944). 14cc2d5: IDEA-114534 Gradle: cancelation of gradle tasks for "out-of-process" mode 4684ba7: Add package-info.java checking to "Declaration has problems in Javadoc references" inspection 53c1eff: [log] Move logProvider extension point to the right place: vcs-log.xml b667798: decode file name for quickfix #WEB-9559 fixed bb34511: new inference: ignore inferred wildcards in return position 74aa35f: new inference: postpone resolve as long as possible 506188d: new inference: mark unresolved calls as poly expressions 3b99b77: Refactor annotator to use visitor 57f9383: Add annotator for inspecting some common errors in JQL queries c889c0a: svn: Refactored vcs roots detection - CopiesDetector moved/renamed to separate class, detection output extracted to separate class cf66975: svn: Forced status command result contain repository root url (resolved with corresponding info command) 2a7389c: svn: Refactored Node class to have notnull fields 69d918e: svn: Refactored WCInfo - utilize RootUrlInfo on which WCInfo is based (instead of just copying fields) a9da4bc: svn: Refactored RootUrlInfo - removed setType() usages where possible 5f09493: svn: Refactored nested copy detection - removed unused code 6eee591: svn: Refactored nested copy detection - extractions, renames, simplified logic b3eeb5e: svn: Refactored nested copy detection - use instance instead of static methods 238c879: svn: Refactored nested copy detection - removed Real class 38db9bf: svn: Refactored nested copy detection - moves, renames dd8b94c: svn: Refactored nested copy detection - simplified data structures for storing nested copies (while collecting changes with ChangeProvider) 8fbc758: svn: Refactored filtering roots that are not actually working copies - methods extracted, logic simplified 66608ee: svn: Refactored inner/external working copies detection - removed unused code 06f90ae: svn: Refactored inner/external working copies detection - methods extracted, logic simplified b44d86f: svn: Refactored nested copies detection by folder status - simplified code flow e397390: svn: Replaced working copy format detection using SVNStatus.getWorkingCopyFormat() with SvnVcs.getWorkingCopyFormat() (1.7 and 1.8 both have "12" entries file format and previously 1.7 was always returned for command line status command) f0a4f49: svn: Refactored working copy format detection for rollback missing files/directories - do not use SVNStatus.getWorkingCopyFormat() 0bdd518: svn: Refactored getting changelist name from SVNStatus - removed duplication and check if working copy format supports change lists 054bf7f: svn: Refactored CommandUtil.escape (simplified) e211be2: svn: Removed unused WorkingCopyFormat parameter from StatusReceiver 8d5fffd: svn: Fixed "upgrade working copy format" dialog - always make current working copy format enabled Change-Id: I928a2caf9bf9d566fb9f556ce39acb5111119c9c --- ...ooleanMethodIsAlwaysInvertedInspectionBase.java | 179 ++++ .../src/com/siyeh/ig/BaseInspection.java | 6 +- .../com/siyeh/ig/BaseSharedLocalInspection.java | 45 + .../ig/bugs/MisspelledCompareToInspectionBase.java | 63 ++ .../ig/bugs/MisspelledEqualsInspectionBase.java | 62 ++ .../ig/bugs/MisspelledHashcodeInspectionBase.java | 63 ++ .../ig/bugs/MisspelledToStringInspectionBase.java | 63 ++ .../AnonymousInnerClassInspectionBase.java | 68 ++ .../ClassInTopLevelPackageInspectionBase.java | 81 ++ ...ClassNameDiffersFromFileNameInspectionBase.java | 78 ++ .../ig/classlayout/EmptyClassInspectionBase.java | 172 ++++ .../InnerClassOnInterfaceInspectionBase.java | 94 +++ .../LimitedScopeInnerClassInspectionBase.java | 60 ++ ...ultipleTopLevelClassesInFileInspectionBase.java | 77 ++ .../NonFinalUtilityClassInspectionBase.java | 61 ++ .../ProtectedMemberInFinalClassInspectionBase.java | 75 ++ .../PublicConstructorInspectionBase.java | 101 +++ .../ig/classlayout/UtilityClassInspectionBase.java | 72 ++ ...assWithoutPrivateConstructorInspectionBase.java | 151 ++++ .../BadExceptionCaughtInspectionBase.java | 112 +++ .../BadExceptionDeclaredInspectionBase.java | 118 +++ .../BadExceptionThrownInspectionBase.java | 105 +++ .../ThrowsRuntimeExceptionInspectionBase.java | 66 ++ .../errorhandling/TooBroadCatchInspectionBase.java | 182 ++++ .../UnusedCatchParameterInspectionBase.java | 126 +++ .../ig/imports/StaticImportInspectionBase.java | 317 +++++++ .../ExtendsConcreteCollectionInspectionBase.java | 74 ++ .../siyeh/ig/inheritance/StaticInheritanceFix.java | 163 ++++ .../inheritance/StaticInheritanceInspection.java | 102 +++ ...anceVariableUninitializedUseInspectionBase.java | 157 ++++ ...CallDuringObjectConstructionInspectionBase.java | 75 ++ ...thodCallDuringObjectConstructionInspection.java | 81 ++ .../MagicCharacterInspectionBase.java | 80 ++ ...onymousInnerClassMayBeStaticInspectionBase.java | 74 ++ .../MethodCallInLoopConditionInspectionBase.java | 92 +++ .../ig/javadoc/MissingPackageInfoInspection.java | 80 +- ...ckageDotHtmlMayBePackageInfoInspectionBase.java | 86 ++ .../siyeh/ig/jdk/AssertAsNameInspectionBase.java | 101 +++ .../com/siyeh/ig/jdk/EnumAsNameInspectionBase.java | 101 +++ ...ClassIsPublicStaticVoidNoArgInspectionBase.java | 88 ++ ...otatedMethodInJUnit3TestCaseInspectionBase.java | 76 ++ ...actTestClassNamingConventionInspectionBase.java | 115 +++ ...nitTestClassNamingConventionInspectionBase.java | 128 +++ .../ig/junit/MisspelledSetUpInspectionBase.java | 72 ++ .../ig/junit/MisspelledTearDownInspectionBase.java | 72 ++ ...edParametersStaticCollectionInspectionBase.java | 129 +++ .../junit/TestCaseInProductCodeInspectionBase.java | 72 ++ .../TestMethodWithoutAssertionInspectionBase.java | 255 ++++++ .../ig/junit/UseOfObsoleteAssertInspection.java | 236 ++++++ .../ClassWithMultipleLoggersInspectionBase.java | 111 +++ .../logging/ClassWithoutLoggerInspectionBase.java | 119 +++ ...atementGuardedByLogConditionInspectionBase.java | 273 ++++++ ...rInitializedWithForeignClassInspectionBase.java | 202 +++++ ...nditionDisagreesWithLogStatementInspection.java | 355 ++++++++ .../NonStaticFinalLoggerInspectionBase.java | 112 +++ ...eholderCountMatchesArgumentCountInspection.java | 128 +++ .../PublicMethodWithoutLoggingInspectionBase.java | 142 ++++ ...ngConcatenationArgumentToLogCallInspection.java | 303 +++++++ .../siyeh/ig/maturity/TodoCommentInspection.java | 54 ++ .../src/com/siyeh/ig/maturity/TodoUtil.java | 42 + ...eroLengthArrayInitializationInspectionBase.java | 91 ++ ...eldAccessReplaceableByMethodCallInspection.java | 223 +++++ .../EnumerationCanBeIterationInspectionBase.java | 195 +++++ .../migration/ForCanBeForeachInspectionBase.java | 920 +++++++++++++++++++++ .../ig/migration/IfCanBeSwitchInspection.java | 547 ++++++++++++ .../IndexOfReplaceableByContainsInspection.java | 272 ++++++ .../MethodCanBeVariableArityMethodInspection.java | 158 ++++ .../RawUseOfParameterizedTypeInspection.java | 189 +++++ ...BufferReplaceableByStringBuilderInspection.java | 214 +++++ .../TryFinallyCanBeTryWithResourcesInspection.java | 568 +++++++++++++ .../TryWithIdenticalCatchesInspection.java | 247 ++++++ .../ig/migration/UnnecessaryBoxingInspection.java | 336 ++++++++ .../migration/UnnecessaryUnboxingInspection.java | 325 ++++++++ .../migration/WhileCanBeForeachInspectionBase.java | 337 ++++++++ .../AnnotationNamingConventionInspectionBase.java | 93 +++ ...hodNameMustStartWithQuestionInspectionBase.java | 123 +++ ...sNamePrefixedWithPackageNameInspectionBase.java | 90 ++ .../ClassNameSameAsAncestorNameInspectionBase.java | 94 +++ .../ClassNamingConventionInspectionBase.java | 98 +++ .../naming/ConfusingMainMethodInspectionBase.java | 88 ++ .../ConstantNamingConventionInspectionBase.java | 103 +++ .../com/siyeh/ig/naming/ConventionInspection.java | 96 +++ .../ig/naming/DollarSignInNameInspectionBase.java | 89 ++ ...meratedClassNamingConventionInspectionBase.java | 93 +++ ...atedConstantNamingConventionInspectionBase.java | 86 ++ ...onNameDoesntEndWithExceptionInspectionBase.java | 83 ++ ...stanceMethodNamingConventionInspectionBase.java | 102 +++ ...anceVariableNamingConventionInspectionBase.java | 95 +++ .../InterfaceNamingConventionInspectionBase.java | 93 +++ ...ocalVariableNamingConventionInspectionBase.java | 133 +++ .../MethodNameSameAsClassNameInspectionBase.java | 74 ++ .../MethodNameSameAsParentNameInspectionBase.java | 81 ++ .../MethodNamesDifferOnlyByCaseInspectionBase.java | 87 ++ ...dNameMayNotStartWithQuestionInspectionBase.java | 125 +++ ...ceptionNameEndsWithExceptionInspectionBase.java | 74 ++ ...ethodsWithSameNumberOfParametersInspection.java | 108 +++ .../naming/OverloadedVarargsMethodInspection.java | 75 ++ .../naming/PackageNamingConventionInspection.java | 186 +++++ ...ffersFromOverriddenParameterInspectionBase.java | 115 +++ .../ParameterNamingConventionInspectionBase.java | 103 +++ .../ig/naming/QuestionableNameInspectionBase.java | 113 +++ .../StandardVariableNamesInspectionBase.java | 155 ++++ ...StaticMethodNamingConventionInspectionBase.java | 92 +++ ...aticVariableNamingConventionInspectionBase.java | 114 +++ ...ypeParameterNamingConventionInspectionBase.java | 96 +++ ...pperCaseFieldNameNotConstantInspectionBase.java | 76 ++ ...yComplexArithmeticExpressionInspectionBase.java | 156 ++++ ...ReplaceableByCompiledPatternInspectionBase.java | 102 +++ ...lWithZeroLengthArrayArgumentInspectionBase.java | 105 +++ ...ithoutPublicNoArgConstructorInspectionBase.java | 93 +++ ...ableFieldInSerializableClassInspectionBase.java | 95 +++ ...ableHasSerialVersionUIDFieldInspectionBase.java | 90 ++ ...zableHasSerializationMethodsInspectionBase.java | 96 +++ ...lassHasSerialVersionUIDFieldInspectionBase.java | 56 ++ ...eInnerClassHasSerialVersionUIDFieldVisitor.java | 79 ++ ...ithNonSerializableOuterClassInspectionBase.java | 43 + ...rClassWithNonSerializableOuterClassVisitor.java | 63 ++ .../serialization/SerializableInspectionBase.java | 71 ++ .../ig/style/ChainedMethodCallInspectionBase.java | 91 ++ .../ig/style/NestedMethodCallInspectionBase.java | 88 ++ .../SizeReplaceableByIsEmptyInspectionBase.java | 213 +++++ .../UnqualifiedInnerClassAccessInspectionBase.java | 135 +++ .../ig/threading/ExtendsThreadInspectionBase.java | 72 ++ 123 files changed, 16436 insertions(+), 14 deletions(-) create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/intellij/codeInspection/booleanIsAlwaysInverted/BooleanMethodIsAlwaysInvertedInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseSharedLocalInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledCompareToInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledEqualsInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledHashcodeInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledToStringInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/AnonymousInnerClassInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassNameDiffersFromFileNameInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/EmptyClassInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/InnerClassOnInterfaceInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/LimitedScopeInnerClassInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MultipleTopLevelClassesInFileInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/NonFinalUtilityClassInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ProtectedMemberInFinalClassInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/PublicConstructorInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassWithoutPrivateConstructorInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionCaughtInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionDeclaredInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionThrownInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/ThrowsRuntimeExceptionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/TooBroadCatchInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnusedCatchParameterInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/StaticImportInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/ExtendsConcreteCollectionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/StaticInheritanceFix.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/StaticInheritanceInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/InstanceVariableUninitializedUseInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/OverridableMethodCallDuringObjectConstructionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/OverriddenMethodCallDuringObjectConstructionInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/internationalization/MagicCharacterInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/PackageDotHtmlMayBePackageInfoInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/jdk/AssertAsNameInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/jdk/EnumAsNameInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/BeforeClassOrAfterClassIsPublicStaticVoidNoArgInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnit4AnnotatedMethodInJUnit3TestCaseInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitAbstractTestClassNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitTestClassNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisspelledSetUpInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisspelledTearDownInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/ParameterizedParametersStaticCollectionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestCaseInProductCodeInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestMethodWithoutAssertionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/UseOfObsoleteAssertInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/ClassWithMultipleLoggersInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/ClassWithoutLoggerInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LogStatementGuardedByLogConditionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LoggerInitializedWithForeignClassInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LoggingConditionDisagreesWithLogStatementInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/NonStaticFinalLoggerInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/PlaceholderCountMatchesArgumentCountInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/PublicMethodWithoutLoggingInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/StringConcatenationArgumentToLogCallInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/maturity/TodoCommentInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/maturity/TodoUtil.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/ZeroLengthArrayInitializationInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/CollectionsFieldAccessReplaceableByMethodCallInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/EnumerationCanBeIterationInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/ForCanBeForeachInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/IfCanBeSwitchInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/IndexOfReplaceableByContainsInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/MethodCanBeVariableArityMethodInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/RawUseOfParameterizedTypeInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/StringBufferReplaceableByStringBuilderInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/UnnecessaryBoxingInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/UnnecessaryUnboxingInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/AnnotationNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNamePrefixedWithPackageNameInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNameSameAsAncestorNameInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConfusingMainMethodInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConstantNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConventionInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/DollarSignInNameInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/EnumeratedClassNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/EnumeratedConstantNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ExceptionNameDoesntEndWithExceptionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InstanceMethodNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InstanceVariableNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InterfaceNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/LocalVariableNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNameSameAsClassNameInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNameSameAsParentNameInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNamesDifferOnlyByCaseInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonExceptionNameEndsWithExceptionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/OverloadedMethodsWithSameNumberOfParametersInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/OverloadedVarargsMethodInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/PackageNamingConventionInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ParameterNameDiffersFromOverriddenParameterInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ParameterNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/QuestionableNameInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StandardVariableNamesInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StaticMethodNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StaticVariableNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/TypeParameterNamingConventionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/UpperCaseFieldNameNotConstantInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/OverlyComplexArithmeticExpressionInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/DynamicRegexReplaceableByCompiledPatternInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/ToArrayCallWithZeroLengthArrayArgumentInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/NonSerializableFieldInSerializableClassInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableHasSerialVersionUIDFieldInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableHasSerializationMethodsInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldVisitor.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassWithNonSerializableOuterClassInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassWithNonSerializableOuterClassVisitor.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/ChainedMethodCallInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/NestedMethodCallInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SizeReplaceableByIsEmptyInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedInnerClassAccessInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/ExtendsThreadInspectionBase.java (limited to 'plugins/InspectionGadgets/InspectionGadgetsAnalysis/src') diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/intellij/codeInspection/booleanIsAlwaysInverted/BooleanMethodIsAlwaysInvertedInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/intellij/codeInspection/booleanIsAlwaysInverted/BooleanMethodIsAlwaysInvertedInspectionBase.java new file mode 100644 index 000000000000..9015fe3aabb4 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/intellij/codeInspection/booleanIsAlwaysInverted/BooleanMethodIsAlwaysInvertedInspectionBase.java @@ -0,0 +1,179 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.booleanIsAlwaysInverted; + +import com.intellij.analysis.AnalysisScope; +import com.intellij.codeInsight.daemon.GroupNames; +import com.intellij.codeInspection.*; +import com.intellij.codeInspection.reference.*; +import com.intellij.openapi.util.Key; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public class BooleanMethodIsAlwaysInvertedInspectionBase extends GlobalJavaBatchInspectionTool { + private static final Key ALWAYS_INVERTED = Key.create("ALWAYS_INVERTED_METHOD"); + + private static boolean hasNonInvertedCalls(final RefMethod refMethod) { + final Boolean alwaysInverted = refMethod.getUserData(ALWAYS_INVERTED); + if (alwaysInverted == null) return true; + if (refMethod.isExternalOverride()) return true; + if (refMethod.isReferenced() && !alwaysInverted.booleanValue()) return true; + final Collection superMethods = refMethod.getSuperMethods(); + for (RefMethod superMethod : superMethods) { + if (hasNonInvertedCalls(superMethod)) return true; + } + return false; + } + + private static void traverseSuperMethods(RefMethod refMethod, + GlobalJavaInspectionContext globalContext, + GlobalJavaInspectionContext.UsagesProcessor processor) { + final Collection superMethods = refMethod.getSuperMethods(); + for (RefMethod superMethod : superMethods) { + traverseSuperMethods(superMethod, globalContext, processor); + } + globalContext.enqueueMethodUsagesProcessor(refMethod, processor); + } + + private static void checkMethodCall(RefElement refWhat, final PsiElement element) { + if (!(refWhat instanceof RefMethod)) return; + final RefMethod refMethod = (RefMethod)refWhat; + final PsiElement psiElement = refMethod.getElement(); + if (!(psiElement instanceof PsiMethod)) return; + final PsiMethod psiMethod = (PsiMethod)psiElement; + if (!PsiType.BOOLEAN.equals(psiMethod.getReturnType())) return; + element.accept(new JavaRecursiveElementVisitor() { + @Override + public void visitMethodCallExpression(PsiMethodCallExpression call) { + super.visitMethodCallExpression(call); + final PsiReferenceExpression methodExpression = call.getMethodExpression(); + if (methodExpression.isReferenceTo(psiMethod)) { + if (isInvertedMethodCall(methodExpression)) return; + refMethod.putUserData(ALWAYS_INVERTED, Boolean.FALSE); + } + } + }); + } + + private static boolean isInvertedMethodCall(final PsiReferenceExpression methodExpression) { + final PsiPrefixExpression prefixExpression = PsiTreeUtil.getParentOfType(methodExpression, PsiPrefixExpression.class); + if (methodExpression.getQualifierExpression() instanceof PsiSuperExpression) return true; //don't flag super calls + if (prefixExpression != null) { + final IElementType tokenType = prefixExpression.getOperationTokenType(); + if (tokenType.equals(JavaTokenType.EXCL)) { + return true; + } + } + return false; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionsBundle.message("boolean.method.is.always.inverted.display.name"); + } + + @Override + @NotNull + public String getGroupDisplayName() { + return GroupNames.DATA_FLOW_ISSUES; + } + + @Override + @NotNull + @NonNls + public String getShortName() { + return "BooleanMethodIsAlwaysInverted"; + } + + @Override + @Nullable + public RefGraphAnnotator getAnnotator(@NotNull final RefManager refManager) { + return new BooleanInvertedAnnotator(); + } + + @Override + public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity, + @NotNull AnalysisScope scope, + @NotNull final InspectionManager manager, + @NotNull final GlobalInspectionContext globalContext) { + if (refEntity instanceof RefMethod) { + RefMethod refMethod = (RefMethod)refEntity; + if (!refMethod.isReferenced()) return null; + if (hasNonInvertedCalls(refMethod)) return null; + if (!refMethod.getSuperMethods().isEmpty()) return null; + final PsiMethod psiMethod = (PsiMethod)refMethod.getElement(); + final PsiIdentifier psiIdentifier = psiMethod.getNameIdentifier(); + if (psiIdentifier != null) { + return new ProblemDescriptor[]{manager.createProblemDescriptor(psiIdentifier, + InspectionsBundle + .message("boolean.method.is.always.inverted.problem.descriptor"), + (LocalQuickFix)getQuickFix(null), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false)}; + } + } + return null; + } + + @Override + protected boolean queryExternalUsagesRequests(@NotNull final RefManager manager, + @NotNull final GlobalJavaInspectionContext context, + @NotNull final ProblemDescriptionsProcessor descriptionsProcessor) { + manager.iterate(new RefJavaVisitor() { + @Override + public void visitMethod(@NotNull final RefMethod refMethod) { + if (descriptionsProcessor.getDescriptions(refMethod) != null) { //suspicious method -> need to check external usages + final GlobalJavaInspectionContext.UsagesProcessor usagesProcessor = new GlobalJavaInspectionContext.UsagesProcessor() { + @Override + public boolean process(PsiReference psiReference) { + final PsiElement psiReferenceExpression = psiReference.getElement(); + if (psiReferenceExpression instanceof PsiReferenceExpression && + !isInvertedMethodCall((PsiReferenceExpression)psiReferenceExpression)) { + descriptionsProcessor.ignoreElement(refMethod); + } + return false; + } + }; + traverseSuperMethods(refMethod, context, usagesProcessor); + } + } + }); + return false; + } + + private static class BooleanInvertedAnnotator extends RefGraphAnnotator { + @Override + public void onInitialize(RefElement refElement) { + if (refElement instanceof RefMethod) { + final PsiElement element = refElement.getElement(); + if (!(element instanceof PsiMethod)) return; + if (((PsiMethod)element).getReturnType() != PsiType.BOOLEAN) return; + refElement.putUserData(ALWAYS_INVERTED, Boolean.TRUE); //initial mark boolean methods + } + } + + @Override + public void onMarkReferenced(RefElement refWhat, RefElement refFrom, boolean referencedFromClassInitializer) { + checkMethodCall(refWhat, refFrom.getElement()); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseInspection.java index cc50b2af1124..d8b12a9f9843 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ public abstract class BaseInspection extends BaseJavaBatchLocalInspectionTool { @Override @NotNull - public final String getShortName() { + public String getShortName() { if (m_shortName == null) { final Class aClass = getClass(); final String name = aClass.getName(); @@ -150,7 +150,7 @@ public abstract class BaseInspection extends BaseJavaBatchLocalInspectionTool { for (List out : outs) { out.clear(); } - int iMax = strings.size(); + final int iMax = strings.size(); for (int i = 0; i < iMax; i += outs.length) { for (int j = 0; j < outs.length; j++) { final List out = outs[j]; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseSharedLocalInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseSharedLocalInspection.java new file mode 100644 index 000000000000..561ed26b4c97 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseSharedLocalInspection.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig; + +import com.intellij.codeInspection.GlobalInspectionTool; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +/** + * @author Bas Leijdekkers + */ +public abstract class BaseSharedLocalInspection extends BaseInspection { + + protected final T mySettingsDelegate; + + public BaseSharedLocalInspection(T settingsDelegate) { + mySettingsDelegate = settingsDelegate; + } + + @NotNull + @Override + public final String getShortName() { + return mySettingsDelegate.getShortName(); + } + + @Nls + @NotNull + @Override + public final String getDisplayName() { + return mySettingsDelegate.getDisplayName(); + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledCompareToInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledCompareToInspectionBase.java new file mode 100644 index 000000000000..75a6909949c5 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledCompareToInspectionBase.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameterList; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class MisspelledCompareToInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "misspelled.compareto.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "misspelled.compareto.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MisspelledCompareToVisitor(); + } + + private static class MisspelledCompareToVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + //note: no call to super + @NonNls final String methodName = method.getName(); + if (!"compareto".equals(methodName)) { + return; + } + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() != 1) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledEqualsInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledEqualsInspectionBase.java new file mode 100644 index 000000000000..8bba89165852 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledEqualsInspectionBase.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameterList; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class MisspelledEqualsInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "misspelled.equals.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "misspelled.equals.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MisspelledEqualsVisitor(); + } + + private static class MisspelledEqualsVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + //note: no call to super + @NonNls final String methodName = method.getName(); + if (!"equal".equals(methodName)) { + return; + } + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() != 1) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledHashcodeInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledHashcodeInspectionBase.java new file mode 100644 index 000000000000..435cfed288d0 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledHashcodeInspectionBase.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameterList; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class MisspelledHashcodeInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "misspelled.hashcode.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "misspelled.hashcode.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MisspelledHashcodeVisitor(); + } + + private static class MisspelledHashcodeVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + //note: no call to super + @NonNls final String methodName = method.getName(); + if (!"hashcode".equals(methodName)) { + return; + } + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() != 0) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledToStringInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledToStringInspectionBase.java new file mode 100644 index 000000000000..4fd6e42d13f3 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MisspelledToStringInspectionBase.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameterList; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class MisspelledToStringInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "misspelled.tostring.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "misspelled.tostring.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MisspelledToStringVisitor(); + } + + private static class MisspelledToStringVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + //note: no call to super + @NonNls final String methodName = method.getName(); + if (!"tostring".equals(methodName)) { + return; + } + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() != 0) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/AnonymousInnerClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/AnonymousInnerClassInspectionBase.java new file mode 100644 index 000000000000..38222c79290d --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/AnonymousInnerClassInspectionBase.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.psi.PsiAnonymousClass; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiEnumConstantInitializer; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class AnonymousInnerClassInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "anonymous.inner.class.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "anonymous.inner.class.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new AnonymousInnerClassVisitor(); + } + + private static class AnonymousInnerClassVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + //no call to super here, to avoid double counting + } + + @Override + public void visitAnonymousClass(@NotNull PsiAnonymousClass aClass) { + super.visitAnonymousClass(aClass); + if (aClass instanceof PsiEnumConstantInitializer) { + return; + } + registerClassError(aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java new file mode 100644 index 000000000000..dec9e2e662d2 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiJavaFile; +import com.intellij.psi.util.FileTypeUtils; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ClassUtils; +import org.jetbrains.annotations.NotNull; + +public class ClassInTopLevelPackageInspectionBase extends BaseInspection { + @Override + @NotNull + public String getID() { + return "ClassWithoutPackageStatement"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "class.in.top.level.package.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "class.in.top.level.package.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ClassInTopLevelPackageVisitor(); + } + + private static class ClassInTopLevelPackageVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so that it doesn't drill down to inner classes + if (FileTypeUtils.isInServerPageFile(aClass)) { + return; + } + if (ClassUtils.isInnerClass(aClass)) { + return; + } + final PsiFile file = aClass.getContainingFile(); + if (!(file instanceof PsiJavaFile)) { + return; + } + if (((PsiJavaFile)file).getPackageStatement() != null) { + return; + } + registerClassError(aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassNameDiffersFromFileNameInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassNameDiffersFromFileNameInspectionBase.java new file mode 100644 index 000000000000..3726e6aa38ea --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassNameDiffersFromFileNameInspectionBase.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiJavaFile; +import com.intellij.psi.util.FileTypeUtils; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class ClassNameDiffersFromFileNameInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "class.name.differs.from.file.name.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "class.name.differs.from.file.name.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ClassNameDiffersFromFileNameVisitor(); + } + + private static class ClassNameDiffersFromFileNameVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so that it doesn't drill down to inner classes + if (FileTypeUtils.isInServerPageFile(aClass)) { + return; + } + final PsiElement parent = aClass.getParent(); + if (!(parent instanceof PsiJavaFile)) { + return; + } + final PsiJavaFile file = (PsiJavaFile)parent; + final String className = aClass.getName(); + if (className == null) { + return; + } + final String fileName = file.getName(); + final int prefixIndex = fileName.indexOf((int)'.'); + if (prefixIndex < 0) { + return; + } + final String filenameWithoutPrefix = + fileName.substring(0, prefixIndex); + if (className.equals(filenameWithoutPrefix)) { + return; + } + registerClassError(aClass, file); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/EmptyClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/EmptyClassInspectionBase.java new file mode 100644 index 000000000000..490f08156919 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/EmptyClassInspectionBase.java @@ -0,0 +1,172 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.psi.*; +import com.intellij.psi.util.FileTypeUtils; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.fixes.AddToIgnoreIfAnnotatedByListQuickFix; +import com.siyeh.ig.ui.ExternalizableStringSet; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class EmptyClassInspectionBase extends BaseInspection { + @SuppressWarnings({"PublicField"}) + public final ExternalizableStringSet ignorableAnnotations = new ExternalizableStringSet(); + @SuppressWarnings({"PublicField"}) + public boolean ignoreClassWithParameterization = false; + @SuppressWarnings({"PublicField"}) + public boolean ignoreThrowables = true; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("empty.class.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + final Object element = infos[0]; + if (element instanceof PsiAnonymousClass) { + return InspectionGadgetsBundle.message("empty.anonymous.class.problem.descriptor"); + } + else if (element instanceof PsiClass) { + return InspectionGadgetsBundle.message("empty.class.problem.descriptor"); + } + else { + return InspectionGadgetsBundle.message("empty.class.file.without.class.problem.descriptor"); + } + } + + @NotNull + @Override + protected InspectionGadgetsFix[] buildFixes(Object... infos) { + final Object info = infos[0]; + if (!(info instanceof PsiModifierListOwner)) { + return InspectionGadgetsFix.EMPTY_ARRAY; + } + return AddToIgnoreIfAnnotatedByListQuickFix.build((PsiModifierListOwner)info, ignorableAnnotations); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new EmptyClassVisitor(); + } + + private class EmptyClassVisitor extends BaseInspectionVisitor { + + @Override + public void visitFile(PsiFile file) { + if (!(file instanceof PsiJavaFile)) { + return; + } + final PsiJavaFile javaFile = (PsiJavaFile)file; + if (javaFile.getClasses().length != 0) { + return; + } + @NonNls final String fileName = javaFile.getName(); + if ("package-info.java".equals(fileName)) { + return; + } + registerError(file, file); + } + + @Override + public void visitClass(@NotNull PsiClass aClass) { + //don't call super, to prevent drilldown + if (FileTypeUtils.isInServerPageFile(aClass.getContainingFile())) { + return; + } + if (aClass.isInterface() || aClass.isEnum() || aClass.isAnnotationType()) { + return; + } + if (aClass instanceof PsiTypeParameter) { + return; + } + final PsiMethod[] constructors = aClass.getConstructors(); + if (constructors.length > 0) { + return; + } + final PsiMethod[] methods = aClass.getMethods(); + if (methods.length > 0) { + return; + } + final PsiField[] fields = aClass.getFields(); + if (fields.length > 0) { + return; + } + final PsiClassInitializer[] initializers = aClass.getInitializers(); + if (initializers.length > 0) { + return; + } + if (ignoreClassWithParameterization && isSuperParametrization(aClass)) { + return; + } + if (AnnotationUtil.isAnnotated(aClass, ignorableAnnotations)) { + return; + } + if (ignoreThrowables && InheritanceUtil.isInheritor(aClass, "java.lang.Throwable")) { + return; + } + registerClassError(aClass, aClass); + } + + private boolean hasTypeArguments(PsiReferenceList extendsList) { + if (extendsList == null) { + return false; + } + final PsiJavaCodeReferenceElement[] referenceElements = extendsList.getReferenceElements(); + for (PsiJavaCodeReferenceElement referenceElement : referenceElements) { + final PsiReferenceParameterList parameterList = referenceElement.getParameterList(); + if (parameterList == null) { + continue; + } + final PsiType[] typeArguments = parameterList.getTypeArguments(); + if (typeArguments.length != 0) { + return true; + } + } + return false; + } + + private boolean isSuperParametrization(PsiClass aClass) { + if (!(aClass instanceof PsiAnonymousClass)) { + final PsiReferenceList extendsList = aClass.getExtendsList(); + final PsiReferenceList implementsList = aClass.getImplementsList(); + return hasTypeArguments(extendsList) || hasTypeArguments(implementsList); + } + final PsiAnonymousClass anonymousClass = (PsiAnonymousClass)aClass; + final PsiJavaCodeReferenceElement reference = anonymousClass.getBaseClassReference(); + final PsiReferenceParameterList parameterList = reference.getParameterList(); + if (parameterList == null) { + return false; + } + final PsiTypeElement[] elements = parameterList.getTypeParameterElements(); + for (PsiTypeElement element : elements) { + if (element != null) { + return true; + } + } + return false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/InnerClassOnInterfaceInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/InnerClassOnInterfaceInspectionBase.java new file mode 100644 index 000000000000..5f4dbd1a0d36 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/InnerClassOnInterfaceInspectionBase.java @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.psi.PsiAnonymousClass; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiTypeParameter; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class InnerClassOnInterfaceInspectionBase extends BaseInspection { + /** + * @noinspection PublicField + */ + public boolean m_ignoreInnerInterfaces = false; + + @Override + @NotNull + public String getID() { + return "InnerClassOfInterface"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "inner.class.on.interface.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final PsiClass parentInterface = (PsiClass)infos[0]; + final String interfaceName = parentInterface.getName(); + return InspectionGadgetsBundle.message( + "inner.class.on.interface.problem.descriptor", interfaceName); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new InnerClassOnInterfaceVisitor(); + } + + private class InnerClassOnInterfaceVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so that it doesn't drill down to inner classes + if (!aClass.isInterface() || aClass.isAnnotationType()) { + return; + } + final PsiClass[] innerClasses = aClass.getInnerClasses(); + for (final PsiClass innerClass : innerClasses) { + if (isInnerClass(innerClass)) { + registerClassError(innerClass, aClass); + } + } + } + + private boolean isInnerClass(PsiClass innerClass) { + if (innerClass.isEnum()) { + return false; + } + if (innerClass.isAnnotationType()) { + return false; + } + if (innerClass instanceof PsiTypeParameter || + innerClass instanceof PsiAnonymousClass) { + return false; + } + return !(innerClass.isInterface() && m_ignoreInnerInterfaces); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/LimitedScopeInnerClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/LimitedScopeInnerClassInspectionBase.java new file mode 100644 index 000000000000..a35a0336a281 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/LimitedScopeInnerClassInspectionBase.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiDeclarationStatement; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class LimitedScopeInnerClassInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "limited.scope.inner.class.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "limited.scope.inner.class.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new LimitedScopeInnerClassVisitor(); + } + + private static class LimitedScopeInnerClassVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (aClass.getParent() instanceof PsiDeclarationStatement) { + registerClassError(aClass); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MultipleTopLevelClassesInFileInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MultipleTopLevelClassesInFileInspectionBase.java new file mode 100644 index 000000000000..7a14ddfade53 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MultipleTopLevelClassesInFileInspectionBase.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiJavaFile; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class MultipleTopLevelClassesInFileInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "multiple.top.level.classes.in.file.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "multiple.top.level.classes.in.file.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MultipleTopLevelClassesInFileVisitor(); + } + + private static class MultipleTopLevelClassesInFileVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so that it doesn't drill down to inner classes + if (!(aClass.getParent() instanceof PsiJavaFile)) { + return; + } + final PsiJavaFile file = (PsiJavaFile)aClass.getParent(); + if (file == null) { + return; + } + int numClasses = 0; + final PsiElement[] children = file.getChildren(); + for (final PsiElement child : children) { + if (child instanceof PsiClass) { + numClasses++; + } + } + if (numClasses <= 1) { + return; + } + registerClassError(aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/NonFinalUtilityClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/NonFinalUtilityClassInspectionBase.java new file mode 100644 index 000000000000..f8a156a3e708 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/NonFinalUtilityClassInspectionBase.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiModifier; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.UtilityClassUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +public class NonFinalUtilityClassInspectionBase extends BaseInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("non.final.utility.class.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("non.final.utility.class.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NonFinalUtilityClassVisitor(); + } + + private static class NonFinalUtilityClassVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so that it doesn't drill down to inner classes + if (!UtilityClassUtil.isUtilityClass(aClass)) { + return; + } + if (aClass.hasModifierProperty(PsiModifier.FINAL) || + aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + return; + } + registerClassError(aClass, aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ProtectedMemberInFinalClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ProtectedMemberInFinalClassInspectionBase.java new file mode 100644 index 000000000000..c5da22a20be1 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ProtectedMemberInFinalClassInspectionBase.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiModifier; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.MethodUtils; +import org.jetbrains.annotations.NotNull; + +public class ProtectedMemberInFinalClassInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("protected.member.in.final.class.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("protected.member.in.final.class.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ProtectedMemberInFinalClassVisitor(); + } + + private static class ProtectedMemberInFinalClassVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + if (!method.hasModifierProperty(PsiModifier.PROTECTED)) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null || !containingClass.hasModifierProperty(PsiModifier.FINAL)) { + return; + } + if (MethodUtils.hasSuper(method)) { + return; + } + registerModifierError(PsiModifier.PROTECTED, method, PsiModifier.PROTECTED); + } + + @Override + public void visitField(@NotNull PsiField field) { + if (!field.hasModifierProperty(PsiModifier.PROTECTED)) { + return; + } + final PsiClass containingClass = field.getContainingClass(); + if (containingClass == null || !containingClass.hasModifierProperty(PsiModifier.FINAL)) { + return; + } + registerModifierError(PsiModifier.PROTECTED, field, PsiModifier.PROTECTED); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/PublicConstructorInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/PublicConstructorInspectionBase.java new file mode 100644 index 000000000000..ef9c1fbcebea --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/PublicConstructorInspectionBase.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiParameterList; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.SerializationUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +public class PublicConstructorInspectionBase extends BaseInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("public.constructor.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + if (((Boolean)infos[0]).booleanValue()) { + return InspectionGadgetsBundle.message("public.default.constructor.problem.descriptor"); + } + else { + return InspectionGadgetsBundle.message("public.constructor.problem.descriptor"); + } + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new PublicConstructorVisitor(); + } + + private static class PublicConstructorVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(PsiMethod method) { + super.visitMethod(method); + if (!method.isConstructor()) { + return; + } + if (!method.hasModifierProperty(PsiModifier.PUBLIC)) { + return; + } + final PsiClass aClass = method.getContainingClass(); + if (aClass == null || aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + return; + } + if (SerializationUtils.isExternalizable(aClass)) { + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() == 0) { + return; + } + } + registerMethodError(method, Boolean.FALSE); + } + + @Override + public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + if (aClass.isInterface() || aClass.isEnum()) { + return; + } + if (!aClass.hasModifierProperty(PsiModifier.PUBLIC) || aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + return; + } + final PsiMethod[] constructors = aClass.getConstructors(); + if (constructors.length > 0) { + return; + } + if (SerializationUtils.isExternalizable(aClass)) { + return; + } + registerClassError(aClass, Boolean.TRUE); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassInspectionBase.java new file mode 100644 index 000000000000..a820a19ca93d --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassInspectionBase.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiModifierListOwner; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.fixes.AddToIgnoreIfAnnotatedByListQuickFix; +import com.siyeh.ig.psiutils.UtilityClassUtil; +import com.siyeh.ig.ui.ExternalizableStringSet; +import org.jetbrains.annotations.NotNull; + +public class UtilityClassInspectionBase extends BaseInspection { + @SuppressWarnings({"PublicField"}) + public final ExternalizableStringSet ignorableAnnotations = new ExternalizableStringSet(); + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("utility.class.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "utility.class.problem.descriptor"); + } + + @NotNull + @Override + protected InspectionGadgetsFix[] buildFixes(Object... infos) { + return AddToIgnoreIfAnnotatedByListQuickFix.build((PsiModifierListOwner)infos[0], ignorableAnnotations); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UtilityClassVisitor(); + } + + private class UtilityClassVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so that it doesn't drill down to inner classes + if (!UtilityClassUtil.isUtilityClass(aClass)) { + return; + } + if (AnnotationUtil.isAnnotated(aClass, ignorableAnnotations)) { + return; + } + registerClassError(aClass, aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassWithoutPrivateConstructorInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassWithoutPrivateConstructorInspectionBase.java new file mode 100644 index 000000000000..67db7be23a02 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassWithoutPrivateConstructorInspectionBase.java @@ -0,0 +1,151 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.classlayout; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.searches.ClassInheritorsSearch; +import com.intellij.util.Query; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.UtilityClassUtil; +import com.siyeh.ig.ui.ExternalizableStringSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class UtilityClassWithoutPrivateConstructorInspectionBase extends BaseInspection { + @SuppressWarnings({"PublicField"}) + public final ExternalizableStringSet ignorableAnnotations = new ExternalizableStringSet(); + @SuppressWarnings({"PublicField"}) + public boolean ignoreClassesWithOnlyMain = false; + + @Nullable + static PsiMethod getNullArgConstructor(PsiClass aClass) { + final PsiMethod[] constructors = aClass.getConstructors(); + for (final PsiMethod constructor : constructors) { + final PsiParameterList params = constructor.getParameterList(); + if (params.getParametersCount() == 0) { + return constructor; + } + } + return null; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("utility.class.without.private.constructor.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("utility.class.without.private.constructor.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UtilityClassWithoutPrivateConstructorVisitor(); + } + + + private class UtilityClassWithoutPrivateConstructorVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so that it doesn't drill down to inner classes + if (aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + return; + } + if (!UtilityClassUtil.isUtilityClass(aClass)) { + return; + } + if (ignoreClassesWithOnlyMain && hasOnlyMain(aClass)) { + return; + } + if (hasPrivateConstructor(aClass)) { + return; + } + if (AnnotationUtil.isAnnotated(aClass, ignorableAnnotations)) { + return; + } + if (aClass.hasModifierProperty(PsiModifier.PRIVATE) && aClass.getConstructors().length == 0) { + return; + } + final SearchScope scope = GlobalSearchScope.projectScope(aClass.getProject()); + final Query query = ClassInheritorsSearch.search(aClass, scope, true, true); + final PsiClass subclass = query.findFirst(); + if (subclass != null) { + return; + } + registerClassError(aClass, aClass); + } + + private boolean hasOnlyMain(PsiClass aClass) { + final PsiMethod[] methods = aClass.getMethods(); + if (methods.length == 0) { + return false; + } + for (PsiMethod method : methods) { + if (method.isConstructor()) { + continue; + } + if (!method.hasModifierProperty(PsiModifier.STATIC)) { + return false; + } + if (method.hasModifierProperty(PsiModifier.PRIVATE)) { + continue; + } + if (!method.hasModifierProperty(PsiModifier.PUBLIC)) { + return false; + } + final String name = method.getName(); + if (!name.equals(HardcodedMethodConstants.MAIN)) { + return false; + } + final PsiType returnType = method.getReturnType(); + if (!PsiType.VOID.equals(returnType)) { + return false; + } + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() != 1) { + return false; + } + final PsiParameter[] parameters = parameterList.getParameters(); + final PsiParameter parameter = parameters[0]; + final PsiType type = parameter.getType(); + if (!type.equalsToText("java.lang.String[]")) { + return false; + } + } + return true; + } + + boolean hasPrivateConstructor(PsiClass aClass) { + final PsiMethod[] constructors = aClass.getConstructors(); + for (final PsiMethod constructor : constructors) { + if (constructor.hasModifierProperty(PsiModifier.PRIVATE)) { + return true; + } + } + return false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionCaughtInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionCaughtInspectionBase.java new file mode 100644 index 000000000000..c51df92ba104 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionCaughtInspectionBase.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.errorhandling; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiCatchSection; +import com.intellij.psi.PsiParameter; +import com.intellij.psi.PsiType; +import com.intellij.psi.PsiTypeElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.ui.ExternalizableStringSet; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class BadExceptionCaughtInspectionBase extends BaseInspection { + /** + * @noinspection PublicField + */ + public final ExternalizableStringSet exceptions = + new ExternalizableStringSet( + "java.lang.NullPointerException", + "java.lang.IllegalMonitorStateException", + "java.lang.ArrayIndexOutOfBoundsException" + ); + /** + * @noinspection PublicField + */ + public String exceptionsString = ""; + + public BadExceptionCaughtInspectionBase() { + if (!exceptionsString.isEmpty()) { + exceptions.clear(); + final List strings = StringUtil.split(exceptionsString, ","); + for (String string : strings) { + exceptions.add(string); + } + exceptionsString = ""; + } + } + + @Override + @NotNull + public String getID() { + return "ProhibitedExceptionCaught"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("bad.exception.caught.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("bad.exception.caught.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BadExceptionCaughtVisitor(); + } + + private class BadExceptionCaughtVisitor extends BaseInspectionVisitor { + + @Override + public void visitCatchSection(PsiCatchSection section) { + super.visitCatchSection(section); + final PsiParameter parameter = section.getParameter(); + if (parameter == null) { + return; + } + final PsiTypeElement typeElement = parameter.getTypeElement(); + if (typeElement == null) { + return; + } + final PsiTypeElement[] childTypeElements = PsiTreeUtil.getChildrenOfType(typeElement, PsiTypeElement.class); + if (childTypeElements != null) { + for (PsiTypeElement childTypeElement : childTypeElements) { + checkTypeElement(childTypeElement); + } + } + else { + checkTypeElement(typeElement); + } + } + + private void checkTypeElement(PsiTypeElement typeElement) { + final PsiType type = typeElement.getType(); + if (exceptions.contains(type.getCanonicalText())) { + registerError(typeElement); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionDeclaredInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionDeclaredInspectionBase.java new file mode 100644 index 000000000000..3cc4dcc1e067 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionDeclaredInspectionBase.java @@ -0,0 +1,118 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.errorhandling; + +import com.intellij.codeInsight.TestFrameworks; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.LibraryUtil; +import com.siyeh.ig.ui.ExternalizableStringSet; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class BadExceptionDeclaredInspectionBase extends BaseInspection { + /** + * @noinspection PublicField + */ + public final ExternalizableStringSet exceptions = + new ExternalizableStringSet( + "java.lang.Throwable", + "java.lang.Exception", + "java.lang.Error", + "java.lang.RuntimeException", + "java.lang.NullPointerException", + "java.lang.ClassCastException", + "java.lang.ArrayIndexOutOfBoundsException" + ); + /** + * @noinspection PublicField + */ + public String exceptionsString = ""; + /** + * @noinspection PublicField + */ + public boolean ignoreTestCases = false; + public boolean ignoreLibraryOverrides = false; + + public BadExceptionDeclaredInspectionBase() { + if (!exceptionsString.isEmpty()) { + exceptions.clear(); + final List strings = StringUtil.split(exceptionsString, ","); + for (String string : strings) { + exceptions.add(string); + } + exceptionsString = ""; + } + } + + @Override + @NotNull + public String getID() { + return "ProhibitedExceptionDeclared"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("bad.exception.declared.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("bad.exception.declared.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BadExceptionDeclaredVisitor(); + } + + private class BadExceptionDeclaredVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + super.visitMethod(method); + if (ignoreTestCases) { + final PsiClass containingClass = method.getContainingClass(); + final TestFrameworks testFrameworks = TestFrameworks.getInstance(); + if (containingClass != null && testFrameworks.isTestOrConfig(containingClass)) { + return; + } + } + if (ignoreLibraryOverrides && LibraryUtil.isOverrideOfLibraryMethod(method)) { + return; + } + final PsiReferenceList throwsList = method.getThrowsList(); + final PsiJavaCodeReferenceElement[] references = throwsList.getReferenceElements(); + for (PsiJavaCodeReferenceElement reference : references) { + final PsiElement element = reference.resolve(); + if (!(element instanceof PsiClass)) { + continue; + } + final PsiClass thrownClass = (PsiClass)element; + final String qualifiedName = thrownClass.getQualifiedName(); + if (qualifiedName != null && exceptions.contains(qualifiedName)) { + registerError(reference); + } + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionThrownInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionThrownInspectionBase.java new file mode 100644 index 000000000000..869bf04baaae --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/BadExceptionThrownInspectionBase.java @@ -0,0 +1,105 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.errorhandling; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiExpression; +import com.intellij.psi.PsiThrowStatement; +import com.intellij.psi.PsiType; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.ui.ExternalizableStringSet; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class BadExceptionThrownInspectionBase extends BaseInspection { + @SuppressWarnings("PublicField") + public final ExternalizableStringSet exceptions = + new ExternalizableStringSet( + "java.lang.Throwable", + "java.lang.Exception", + "java.lang.Error", + "java.lang.RuntimeException", + "java.lang.NullPointerException", + "java.lang.ClassCastException", + "java.lang.ArrayIndexOutOfBoundsException" + ); + /** + * @noinspection PublicField + */ + public String exceptionsString = ""; + + public BadExceptionThrownInspectionBase() { + if (!exceptionsString.isEmpty()) { + exceptions.clear(); + final List strings = + StringUtil.split(exceptionsString, ","); + for (String string : strings) { + exceptions.add(string); + } + exceptionsString = ""; + } + } + + @Override + @NotNull + public String getID() { + return "ProhibitedExceptionThrown"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "bad.exception.thrown.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final PsiType type = (PsiType)infos[0]; + final String exceptionName = type.getPresentableText(); + return InspectionGadgetsBundle.message( + "bad.exception.thrown.problem.descriptor", exceptionName); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BadExceptionThrownVisitor(); + } + + private class BadExceptionThrownVisitor extends BaseInspectionVisitor { + + @Override + public void visitThrowStatement(PsiThrowStatement statement) { + super.visitThrowStatement(statement); + final PsiExpression exception = statement.getException(); + if (exception == null) { + return; + } + final PsiType type = exception.getType(); + if (type == null) { + return; + } + final String text = type.getCanonicalText(); + if (exceptions.contains(text)) { + registerStatementError(statement, type); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/ThrowsRuntimeExceptionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/ThrowsRuntimeExceptionInspectionBase.java new file mode 100644 index 000000000000..9ee31c3f8799 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/ThrowsRuntimeExceptionInspectionBase.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.errorhandling; + +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +public class ThrowsRuntimeExceptionInspectionBase extends BaseInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("throws.runtime.exception.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("throws.runtime.exception.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ThrowsRuntimeExceptionVisitor(); + } + + private static class ThrowsRuntimeExceptionVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(PsiMethod method) { + super.visitMethod(method); + final PsiReferenceList throwsList = method.getThrowsList(); + final PsiJavaCodeReferenceElement[] referenceElements = throwsList.getReferenceElements(); + for (PsiJavaCodeReferenceElement referenceElement : referenceElements) { + final PsiElement target = referenceElement.resolve(); + if (!(target instanceof PsiClass)) { + continue; + } + final PsiClass aClass = (PsiClass)target; + if (!InheritanceUtil.isInheritor(aClass, "java.lang.RuntimeException")) { + continue; + } + final String className = aClass.getName(); + registerError(referenceElement, className, referenceElement); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/TooBroadCatchInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/TooBroadCatchInspectionBase.java new file mode 100644 index 000000000000..cc87b0c73224 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/TooBroadCatchInspectionBase.java @@ -0,0 +1,182 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.errorhandling; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ClassUtils; +import com.siyeh.ig.psiutils.ExceptionUtils; +import com.siyeh.ig.psiutils.TestUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +public class TooBroadCatchInspectionBase extends BaseInspection { + @SuppressWarnings({"PublicField"}) + public boolean onlyWarnOnRootExceptions = false; + @SuppressWarnings("PublicField") + public boolean ignoreInTestCode = false; + @SuppressWarnings("PublicField") + public boolean ignoreThrown = false; + + protected static TextRange getRangeToSelect(PsiCodeBlock block) { + PsiElement first = block.getFirstBodyElement(); + if (first instanceof PsiWhiteSpace) { + first = first.getNextSibling(); + } + if (first == null) { + final int offset = block.getTextRange().getStartOffset() + 1; + return new TextRange(offset, offset); + } + PsiElement last = block.getLastBodyElement(); + if (last instanceof PsiWhiteSpace) { + last = last.getPrevSibling(); + } + final TextRange textRange; + if (last == null) { + textRange = first.getTextRange(); + } + else { + textRange = last.getTextRange(); + } + return new TextRange(first.getTextRange().getStartOffset(), textRange.getEndOffset()); + } + + @Override + @NotNull + public String getID() { + return "OverlyBroadCatchBlock"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("too.broad.catch.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + final List typesMasked = (List)infos[0]; + String typesMaskedString = typesMasked.get(0).getName(); + if (typesMasked.size() == 1) { + return InspectionGadgetsBundle.message("too.broad.catch.problem.descriptor", typesMaskedString); + } + else { + //Collections.sort(typesMasked); + final int lastTypeIndex = typesMasked.size() - 1; + for (int i = 1; i < lastTypeIndex; i++) { + typesMaskedString += ", "; + typesMaskedString += typesMasked.get(i).getName(); + } + final String lastTypeString = typesMasked.get(lastTypeIndex).getName(); + return InspectionGadgetsBundle.message("too.broad.catch.problem.descriptor1", typesMaskedString, lastTypeString); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new TooBroadCatchVisitor(); + } + + private class TooBroadCatchVisitor extends BaseInspectionVisitor { + + @Override + public void visitTryStatement(@NotNull PsiTryStatement statement) { + super.visitTryStatement(statement); + final PsiCodeBlock tryBlock = statement.getTryBlock(); + if (tryBlock == null) { + return; + } + if (ignoreInTestCode && TestUtils.isInTestCode(statement)) { + return; + } + final Set thrownTypes = ExceptionUtils.calculateExceptionsThrown(tryBlock); + final Set caughtTypes = new HashSet(thrownTypes.size()); + final PsiCatchSection[] catchSections = statement.getCatchSections(); + for (final PsiCatchSection catchSection : catchSections) { + final PsiParameter parameter = catchSection.getParameter(); + if (parameter == null) { + continue; + } + final PsiType caughtType = parameter.getType(); + if (caughtType instanceof PsiDisjunctionType) { + final PsiDisjunctionType disjunctionType = (PsiDisjunctionType)caughtType; + final List types = disjunctionType.getDisjunctions(); + for (PsiType type : types) { + check(thrownTypes, caughtTypes, parameter, type); + } + } + else { + if (thrownTypes.isEmpty()) { + if (CommonClassNames.JAVA_LANG_EXCEPTION.equals(caughtType.getCanonicalText())) { + final PsiTypeElement typeElement = parameter.getTypeElement(); + if (typeElement == null) { + continue; + } + final PsiClass runtimeExceptionClass = ClassUtils.findClass(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION, parameter); + registerError(typeElement, Collections.singletonList(runtimeExceptionClass)); + } + } + else { + check(thrownTypes, caughtTypes, parameter, caughtType); + } + } + } + } + + private void check(Set thrownTypes, Set caughtTypes, PsiParameter parameter, PsiType caughtType) { + final List maskedExceptions = findMaskedExceptions(thrownTypes, caughtTypes, caughtType); + if (maskedExceptions.isEmpty()) { + return; + } + final PsiTypeElement typeElement = parameter.getTypeElement(); + if (typeElement == null) { + return; + } + registerError(typeElement, maskedExceptions); + } + + private List findMaskedExceptions(Set thrownTypes, Set caughtTypes, PsiType caughtType) { + if (thrownTypes.contains(caughtType)) { + if (ignoreThrown) { + return Collections.emptyList(); + } + caughtTypes.add(caughtType); + thrownTypes.remove(caughtType); + } + if (onlyWarnOnRootExceptions) { + if (!ExceptionUtils.isGenericExceptionClass(caughtType)) { + return Collections.emptyList(); + } + } + final List maskedTypes = new ArrayList(); + for (PsiClassType typeThrown : thrownTypes) { + if (!caughtTypes.contains(typeThrown) && caughtType.isAssignableFrom(typeThrown)) { + caughtTypes.add(typeThrown); + final PsiClass aClass = typeThrown.resolve(); + if (aClass != null) { + maskedTypes.add(aClass); + } + } + } + return maskedTypes; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnusedCatchParameterInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnusedCatchParameterInspectionBase.java new file mode 100644 index 000000000000..5c233495efeb --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/UnusedCatchParameterInspectionBase.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.errorhandling; + +import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TestUtils; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class UnusedCatchParameterInspectionBase extends BaseInspection { + /** + * @noinspection PublicField + */ + public boolean m_ignoreCatchBlocksWithComments = false; + /** + * @noinspection PublicField + */ + public boolean m_ignoreTestCases = false; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "unused.catch.parameter.display.name"); + } + + @Override + public JComponent createOptionsPanel() { + final MultipleCheckboxOptionsPanel optionsPanel = + new MultipleCheckboxOptionsPanel(this); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message( + "unused.catch.parameter.ignore.catch.option"), + "m_ignoreCatchBlocksWithComments"); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message( + "unused.catch.parameter.ignore.empty.option"), + "m_ignoreTestCases"); + return optionsPanel; + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + final boolean namedIgnoreButUsed = ((Boolean)infos[0]).booleanValue(); + if (namedIgnoreButUsed) { + return InspectionGadgetsBundle.message( + "used.catch.parameter.named.ignore.problem.descriptor" + ); + } + return InspectionGadgetsBundle.message( + "unused.catch.parameter.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UnusedCatchParameterVisitor(); + } + + private class UnusedCatchParameterVisitor extends BaseInspectionVisitor { + + @Override + public void visitTryStatement(@NotNull PsiTryStatement statement) { + super.visitTryStatement(statement); + if (m_ignoreTestCases && TestUtils.isInTestCode(statement)) { + return; + } + final PsiCatchSection[] catchSections = statement.getCatchSections(); + for (PsiCatchSection catchSection : catchSections) { + checkCatchSection(catchSection); + } + } + + private void checkCatchSection(PsiCatchSection section) { + final PsiParameter parameter = section.getParameter(); + if (parameter == null) { + return; + } + @NonNls final String parameterName = parameter.getName(); + final PsiCodeBlock block = section.getCatchBlock(); + if (block == null) { + return; + } + if (m_ignoreCatchBlocksWithComments) { + final PsiElement[] children = block.getChildren(); + for (final PsiElement child : children) { + if (child instanceof PsiComment) { + return; + } + } + } + final CatchParameterUsedVisitor visitor = + new CatchParameterUsedVisitor(parameter); + block.accept(visitor); + final boolean namedIgnore = PsiUtil.isIgnoredName(parameterName); + if (visitor.isUsed()) { + if (namedIgnore) { + registerVariableError(parameter, Boolean.TRUE); + } + return; + } + else if (namedIgnore) { + return; + } + registerVariableError(parameter, Boolean.FALSE); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/StaticImportInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/StaticImportInspectionBase.java new file mode 100644 index 000000000000..0a1182c80183 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/StaticImportInspectionBase.java @@ -0,0 +1,317 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.imports; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.util.FileTypeUtils; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.OrderedSet; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.StringUtils; +import com.siyeh.ig.psiutils.TestUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class StaticImportInspectionBase extends BaseInspection { + @SuppressWarnings({"PublicField"}) public boolean ignoreSingleFieldImports = false; + @SuppressWarnings({"PublicField"}) public boolean ignoreSingeMethodImports = false; + @SuppressWarnings("PublicField") public boolean ignoreInTestCode = false; + @SuppressWarnings("PublicField") public OrderedSet allowedClasses = new OrderedSet(); + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("static.import.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "static.import.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new StaticImportFix(); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new StaticImportVisitor(); + } + + private static class StaticImportFix extends InspectionGadgetsFix { + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("static.import.replace.quickfix"); + } + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + @Override + public void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiImportStaticStatement importStatement = (PsiImportStaticStatement)descriptor.getPsiElement(); + final PsiJavaCodeReferenceElement importReference = importStatement.getImportReference(); + if (importReference == null) { + return; + } + final JavaResolveResult[] importTargets = importReference.multiResolve(false); + if (importTargets.length == 0) { + return; + } + final boolean onDemand = importStatement.isOnDemand(); + final StaticImportFix.StaticImportReferenceCollector + referenceCollector = new StaticImportFix.StaticImportReferenceCollector(importTargets, onDemand); + final PsiJavaFile file = (PsiJavaFile)importStatement.getContainingFile(); + file.accept(referenceCollector); + final List references = referenceCollector.getReferences(); + final Map referenceTargetMap = new HashMap(); + for (PsiJavaCodeReferenceElement reference : references) { + final PsiElement target = reference.resolve(); + if (target instanceof PsiMember) { + final PsiMember member = (PsiMember)target; + referenceTargetMap.put(reference, member); + } + } + importStatement.delete(); + for (Map.Entry entry : referenceTargetMap.entrySet()) { + removeReference(entry.getKey(), entry.getValue()); + } + } + + private static void removeReference(PsiJavaCodeReferenceElement reference, PsiMember target) { + final PsiManager manager = reference.getManager(); + final Project project = manager.getProject(); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiElementFactory factory = psiFacade.getElementFactory(); + final PsiClass aClass = target.getContainingClass(); + if (aClass == null) { + return; + } + final String qualifiedName = aClass.getQualifiedName(); + final String text = reference.getText(); + final String referenceText = qualifiedName + '.' + text; + if (reference instanceof PsiReferenceExpression) { + final PsiExpression newReference = factory.createExpressionFromText(referenceText, reference); + final PsiElement insertedElement = reference.replace(newReference); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(insertedElement); + } + else { + final PsiJavaCodeReferenceElement referenceElement = + factory.createReferenceElementByFQClassName(referenceText, reference.getResolveScope()); + final PsiElement insertedElement = reference.replace(referenceElement); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(insertedElement); + } + } + + static class StaticImportReferenceCollector extends JavaRecursiveElementVisitor { + + private final JavaResolveResult[] importTargets; + private final boolean onDemand; + private final List references = new ArrayList(); + + StaticImportReferenceCollector(@NotNull JavaResolveResult[] importTargets, boolean onDemand) { + this.importTargets = importTargets; + this.onDemand = onDemand; + } + + @Override + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + super.visitReferenceElement(reference); + if (isFullyQualifiedReference(reference)) { + return; + } + PsiElement parent = reference.getParent(); + if (parent instanceof PsiImportStatementBase) { + return; + } + while (parent instanceof PsiJavaCodeReferenceElement) { + parent = parent.getParent(); + if (parent instanceof PsiImportStatementBase) { + return; + } + } + checkStaticImportReference(reference); + } + + private void checkStaticImportReference(PsiJavaCodeReferenceElement reference) { + if (reference.isQualified()) { + return; + } + final PsiElement target = reference.resolve(); + if (!(target instanceof PsiMethod) && !(target instanceof PsiClass) && !(target instanceof PsiField)) { + return; + } + final PsiMember member = (PsiMember)target; + for (JavaResolveResult importTarget : importTargets) { + final PsiElement targetElement = importTarget.getElement(); + if (targetElement instanceof PsiMethod || targetElement instanceof PsiField) { + if (member.equals(targetElement)) { + addReference(reference); + } + } + else if (targetElement instanceof PsiClass) { + if (onDemand) { + final PsiClass containingClass = member.getContainingClass(); + if (InheritanceUtil.isInheritorOrSelf((PsiClass)targetElement, containingClass, true)) { + addReference(reference); + } + } + else { + if (targetElement.equals(member)) { + addReference(reference); + } + } + } + } + } + + private void addReference(PsiJavaCodeReferenceElement reference) { + references.add(reference); + } + + public List getReferences() { + return references; + } + + public static boolean isFullyQualifiedReference(PsiJavaCodeReferenceElement reference) { + if (!reference.isQualified()) { + return false; + } + final PsiElement directParent = reference.getParent(); + if (directParent instanceof PsiMethodCallExpression || + directParent instanceof PsiAssignmentExpression || + directParent instanceof PsiVariable) { + return false; + } + final PsiElement parent = + PsiTreeUtil.getParentOfType(reference, PsiImportStatementBase.class, PsiPackageStatement.class, JavaCodeFragment.class); + if (parent != null) { + return false; + } + final PsiElement target = reference.resolve(); + if (!(target instanceof PsiClass)) { + return false; + } + final PsiClass aClass = (PsiClass)target; + final String fqName = aClass.getQualifiedName(); + if (fqName == null) { + return false; + } + final String text = + StringUtils.stripAngleBrackets(reference.getText()); + return text.equals(fqName); + } + } + } + + private class StaticImportVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + final PsiElement parent = aClass.getParent(); + if (!(parent instanceof PsiJavaFile)) { + return; + } + final PsiJavaFile file = (PsiJavaFile)parent; + if (FileTypeUtils.isInServerPageFile(file)) { + return; + } + if (!file.getClasses()[0].equals(aClass)) { + return; + } + final PsiImportList importList = file.getImportList(); + if (importList == null) { + return; + } + if (ignoreInTestCode && TestUtils.isTest(aClass)) { + return; + } + final PsiImportStaticStatement[] importStatements = importList.getImportStaticStatements(); + for (PsiImportStaticStatement importStatement : importStatements) { + if (shouldReportImportStatement(importStatement)) { + registerError(importStatement); + } + } + } + + private boolean shouldReportImportStatement(PsiImportStaticStatement importStatement) { + final PsiJavaCodeReferenceElement importReference = importStatement.getImportReference(); + if (importReference == null) { + return false; + } + PsiClass targetClass = importStatement.resolveTargetClass(); + boolean checked = false; + while (targetClass != null) { + final String qualifiedName = targetClass.getQualifiedName(); + if (allowedClasses.contains(qualifiedName)) { + return false; + } + if (checked) { + break; + } + targetClass = targetClass.getContainingClass(); + checked = true; + } + if (importStatement.isOnDemand()) { + return true; + } + if (ignoreSingleFieldImports || ignoreSingeMethodImports) { + boolean field = false; + boolean method = false; + // in the presence of method overloading the plain resolve() method returns null + final JavaResolveResult[] results = importReference.multiResolve(false); + for (JavaResolveResult result : results) { + final PsiElement element = result.getElement(); + if (element instanceof PsiField) { + field = true; + } else if (element instanceof PsiMethod) { + method = true; + } + } + if (field && !method) { + if (ignoreSingleFieldImports) { + return false; + } + } + else if (method && !field) { + if (ignoreSingeMethodImports) { + return false; + } + } + } + return true; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/ExtendsConcreteCollectionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/ExtendsConcreteCollectionInspectionBase.java new file mode 100644 index 000000000000..5a9f275c606a --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/ExtendsConcreteCollectionInspectionBase.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.inheritance; + +import com.intellij.psi.PsiAnonymousClass; +import com.intellij.psi.PsiClass; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.CollectionUtils; +import org.jetbrains.annotations.NotNull; + +public class ExtendsConcreteCollectionInspectionBase extends BaseInspection { + @Override + @NotNull + public String getID() { + return "ClassExtendsConcreteCollection"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "extends.concrete.collection.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final PsiClass superClass = (PsiClass)infos[0]; + final PsiClass aClass = (PsiClass)infos[1]; + if (aClass instanceof PsiAnonymousClass) { + return InspectionGadgetsBundle.message("anonymous.extends.concrete.collection.problem.descriptor", superClass.getQualifiedName()); + } else { + return InspectionGadgetsBundle.message("extends.concrete.collection.problem.descriptor", superClass.getQualifiedName()); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ExtendsConcreteCollectionVisitor(); + } + + private static class ExtendsConcreteCollectionVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isEnum()) { + return; + } + final PsiClass superClass = aClass.getSuperClass(); + if (superClass == null) { + return; + } + if (!CollectionUtils.isCollectionClass(superClass)) { + return; + } + registerClassError(aClass, superClass, aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/StaticInheritanceFix.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/StaticInheritanceFix.java new file mode 100644 index 000000000000..1289bbdbe694 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/StaticInheritanceFix.java @@ -0,0 +1,163 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.inheritance; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.QuickFixFactory; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.*; +import com.intellij.psi.impl.DebugUtil; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.Query; +import com.intellij.util.ui.UIUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ClassUtils; +import org.jetbrains.annotations.NotNull; + +/** + * User: cdr + */ +class StaticInheritanceFix extends InspectionGadgetsFix { + private final boolean myReplaceInWholeProject; + + StaticInheritanceFix(boolean replaceInWholeProject) { + myReplaceInWholeProject = replaceInWholeProject; + } + + @Override + @NotNull + public String getName() { + String scope = + myReplaceInWholeProject ? InspectionGadgetsBundle.message("the.whole.project") : InspectionGadgetsBundle.message("this.class"); + return InspectionGadgetsBundle.message("static.inheritance.replace.quickfix", scope); + } + + @NotNull + @Override + public String getFamilyName() { + return "Replace inheritance with qualified reference"; + } + + @Override + public void doFix(final Project project, final ProblemDescriptor descriptor) throws IncorrectOperationException { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + dodoFix(project, descriptor); + } + }, ModalityState.NON_MODAL, project.getDisposed()); + } + + private void dodoFix(final Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)descriptor.getPsiElement(); + final PsiClass iface = (PsiClass)referenceElement.resolve(); + assert iface != null; + final PsiField[] allFields = iface.getAllFields(); + + final PsiClass implementingClass = ClassUtils.getContainingClass(referenceElement); + final PsiManager manager = referenceElement.getManager(); + assert implementingClass != null; + final PsiFile file = implementingClass.getContainingFile(); + + ProgressManager.getInstance().run(new Task.Modal(project, "Replacing usages of " + iface.getName(), false) { + + @Override + public void run(@NotNull ProgressIndicator indicator) { + for (final PsiField field : allFields) { + final Query search = ReferencesSearch.search(field, implementingClass.getUseScope(), false); + for (PsiReference reference : search) { + if (!(reference instanceof PsiReferenceExpression)) { + continue; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)reference; + if (!myReplaceInWholeProject) { + boolean isInheritor = + ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Boolean compute() { + boolean isInheritor = false; + PsiClass aClass = PsiTreeUtil.getParentOfType(referenceExpression, PsiClass.class); + while (aClass != null) { + isInheritor = InheritanceUtil.isInheritorOrSelf(aClass, implementingClass, true); + if (isInheritor) break; + aClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class); + } + return isInheritor; + } + }); + if (!isInheritor) continue; + } + final Runnable runnable = new Runnable() { + @Override + public void run() { + if (!FileModificationService.getInstance().preparePsiElementsForWrite(referenceExpression)) { + return; + } + final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory(); + final PsiReferenceExpression qualified = + (PsiReferenceExpression)elementFactory + .createExpressionFromText("xxx." + referenceExpression.getText(), referenceExpression); + final PsiReferenceExpression newReference = (PsiReferenceExpression)referenceExpression.replace(qualified); + final PsiReferenceExpression qualifier = (PsiReferenceExpression)newReference.getQualifierExpression(); + assert qualifier != null : DebugUtil.psiToString(newReference, false); + final PsiClass containingClass = field.getContainingClass(); + qualifier.bindToElement(containingClass); + } + }; + invokeWriteAction(runnable, file); + } + } + final Runnable runnable = new Runnable() { + @Override + public void run() { + PsiClassType classType = JavaPsiFacade.getInstance(project).getElementFactory().createType(iface); + IntentionAction fix = QuickFixFactory.getInstance().createExtendsListFix(implementingClass, classType, false); + fix.invoke(project, null, file); + } + }; + invokeWriteAction(runnable, file); + } + }); + } + + private static void invokeWriteAction(final Runnable runnable, final PsiFile file) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + new WriteCommandAction(file.getProject(), file) { + @Override + protected void run(Result result) throws Throwable { + runnable.run(); + } + }.execute(); + } + }); + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/StaticInheritanceInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/StaticInheritanceInspection.java new file mode 100644 index 000000000000..b17cd7d2139e --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/StaticInheritanceInspection.java @@ -0,0 +1,102 @@ +/* + * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.inheritance; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiJavaCodeReferenceElement; +import com.intellij.psi.PsiReferenceList; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +public class StaticInheritanceInspection extends BaseInspection { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "static.inheritance.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "static.inheritance.problem.descriptor"); + } + + @NotNull + @Override + protected InspectionGadgetsFix[] buildFixes(Object... infos) { + return new InspectionGadgetsFix[]{new StaticInheritanceFix(false), new StaticInheritanceFix(true)}; + } + + + @Override + public BaseInspectionVisitor buildVisitor() { + return new StaticInheritanceVisitor(); + } + + private static class StaticInheritanceVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so it doesn't drill down + final PsiReferenceList implementsList = aClass.getImplementsList(); + if (implementsList == null) { + return; + } + final PsiJavaCodeReferenceElement[] references = + implementsList.getReferenceElements(); + for (final PsiJavaCodeReferenceElement reference : references) { + final PsiClass iface = (PsiClass)reference.resolve(); + if (iface != null) { + if (interfaceContainsOnlyConstants(iface, new HashSet())) { + registerError(reference); + } + } + } + } + + private static boolean interfaceContainsOnlyConstants( + PsiClass iface, Set visitedIntefaces) { + if (!visitedIntefaces.add(iface)) { + return true; + } + if (iface.getAllFields().length == 0) { + // ignore it, it's either a true interface or just a marker + return false; + } + if (iface.getMethods().length != 0) { + return false; + } + final PsiClass[] parentInterfaces = iface.getInterfaces(); + for (final PsiClass parentInterface : parentInterfaces) { + if (!interfaceContainsOnlyConstants(parentInterface, + visitedIntefaces)) { + return false; + } + } + return true; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/InstanceVariableUninitializedUseInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/InstanceVariableUninitializedUseInspectionBase.java new file mode 100644 index 000000000000..3b72bd5f87f1 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/InstanceVariableUninitializedUseInspectionBase.java @@ -0,0 +1,157 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.initialization; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.daemon.ImplicitUsageProvider; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.fixes.AddToIgnoreIfAnnotatedByListQuickFix; +import com.siyeh.ig.psiutils.ClassUtils; +import com.siyeh.ig.psiutils.UninitializedReadCollector; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class InstanceVariableUninitializedUseInspectionBase extends BaseInspection { + protected final List annotationNames = new ArrayList(); + /** + * @noinspection PublicField + */ + public boolean m_ignorePrimitives = false; + /** + * @noinspection PublicField + */ + @NonNls + public String annotationNamesString = ""; + + public InstanceVariableUninitializedUseInspectionBase() { + parseString(annotationNamesString, annotationNames); + } + + @Override + @NotNull + public String getID() { + return "InstanceVariableUsedBeforeInitialized"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("instance.variable.used.before.initialized.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("instance.variable.used.before.initialized.problem.descriptor"); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(annotationNamesString, annotationNames); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + annotationNamesString = formatString(annotationNames); + super.writeSettings(element); + } + + @NotNull + @Override + protected InspectionGadgetsFix[] buildFixes(Object... infos) { + final PsiField field = (PsiField)infos[0]; + return AddToIgnoreIfAnnotatedByListQuickFix.build(field, annotationNames); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new InstanceVariableInitializationVisitor(); + } + + private class InstanceVariableInitializationVisitor extends BaseInspectionVisitor { + + @Override + public void visitField(@NotNull PsiField field) { + if (field.hasModifierProperty(PsiModifier.STATIC) || field.hasModifierProperty(PsiModifier.FINAL)) { + return; + } + if (field.getInitializer() != null) { + return; + } + final PsiAnnotation annotation = AnnotationUtil.findAnnotation(field, annotationNames); + if (annotation != null) { + return; + } + if (m_ignorePrimitives) { + final PsiType fieldType = field.getType(); + if (ClassUtils.isPrimitive(fieldType)) { + return; + } + } + final PsiClass aClass = field.getContainingClass(); + if (aClass == null) { + return; + } + for (ImplicitUsageProvider provider : + Extensions.getExtensions(ImplicitUsageProvider.EP_NAME)) { + if (provider.isImplicitWrite(field)) { + return; + } + } + final UninitializedReadCollector uninitializedReadsCollector = new UninitializedReadCollector(); + if (!isInitializedInInitializer(field, uninitializedReadsCollector)) { + final PsiMethod[] constructors = aClass.getConstructors(); + for (final PsiMethod constructor : constructors) { + final PsiCodeBlock body = constructor.getBody(); + uninitializedReadsCollector.blockAssignsVariable(body, field); + } + } + final PsiExpression[] badReads = uninitializedReadsCollector.getUninitializedReads(); + for (PsiExpression expression : badReads) { + registerError(expression, field); + } + } + + private boolean isInitializedInInitializer(@NotNull PsiField field, UninitializedReadCollector uninitializedReadsCollector) { + final PsiClass aClass = field.getContainingClass(); + if (aClass == null) { + return false; + } + final PsiClassInitializer[] initializers = aClass.getInitializers(); + for (final PsiClassInitializer initializer : initializers) { + if (!initializer.hasModifierProperty(PsiModifier.STATIC)) { + final PsiCodeBlock body = initializer.getBody(); + if (uninitializedReadsCollector.blockAssignsVariable(body, field)) { + return true; + } + } + } + return false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/OverridableMethodCallDuringObjectConstructionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/OverridableMethodCallDuringObjectConstructionInspectionBase.java new file mode 100644 index 000000000000..e36fe008ab21 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/OverridableMethodCallDuringObjectConstructionInspectionBase.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.initialization; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.MethodCallUtils; +import org.jetbrains.annotations.NotNull; + +public class OverridableMethodCallDuringObjectConstructionInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("overridable.method.call.in.constructor.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("overridable.method.call.in.constructor.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new OverridableMethodCallInConstructorVisitor(); + } + + private static class OverridableMethodCallInConstructorVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + if (!MethodCallUtils.isCallDuringObjectConstruction(expression)) { + return; + } + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (qualifier != null) { + if (!(qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression)) { + return; + } + } + final PsiClass containingClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class); + if (containingClass == null || containingClass.hasModifierProperty(PsiModifier.FINAL)) { + return; + } + final PsiMethod calledMethod = expression.resolveMethod(); + if (calledMethod == null || !PsiUtil.canBeOverriden(calledMethod)) { + return; + } + final PsiClass calledMethodClass = calledMethod.getContainingClass(); + if (calledMethodClass == null || !calledMethodClass.equals(containingClass)) { + return; + } + registerMethodCallError(expression, expression); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/OverriddenMethodCallDuringObjectConstructionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/OverriddenMethodCallDuringObjectConstructionInspection.java new file mode 100644 index 000000000000..9042da9d041c --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/OverriddenMethodCallDuringObjectConstructionInspection.java @@ -0,0 +1,81 @@ +/* + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.initialization; + +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.MethodCallUtils; +import com.siyeh.ig.psiutils.MethodUtils; +import org.jetbrains.annotations.NotNull; + +public class OverriddenMethodCallDuringObjectConstructionInspection extends BaseInspection { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("overridden.method.call.in.constructor.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("overridden.method.call.in.constructor.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new OverriddenMethodCallInConstructorVisitor(); + } + + private static class OverriddenMethodCallInConstructorVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + if (!MethodCallUtils.isCallDuringObjectConstruction(expression)) { + return; + } + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (qualifier != null) { + if (!(qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression)) { + return; + } + } + final PsiClass containingClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class); + if (containingClass == null || containingClass.hasModifierProperty(PsiModifier.FINAL)) { + return; + } + final PsiMethod calledMethod = expression.resolveMethod(); + if (calledMethod == null || !PsiUtil.canBeOverriden(calledMethod)) { + return; + } + final PsiClass calledMethodClass = calledMethod.getContainingClass(); + if (!InheritanceUtil.isInheritorOrSelf(containingClass, calledMethodClass, true)) { + return; + } + if (!MethodUtils.isOverriddenInHierarchy(calledMethod, containingClass)) { + return; + } + registerMethodCallError(expression); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/internationalization/MagicCharacterInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/internationalization/MagicCharacterInspectionBase.java new file mode 100644 index 000000000000..bbdc674c3b08 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/internationalization/MagicCharacterInspectionBase.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.internationalization; + +import com.intellij.psi.PsiLiteralExpression; +import com.intellij.psi.PsiType; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.jetbrains.annotations.NotNull; + +public class MagicCharacterInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("magic.character.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "magic.character.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new CharacterLiteralsShouldBeExplicitlyDeclaredVisitor(); + } + + private static class CharacterLiteralsShouldBeExplicitlyDeclaredVisitor + extends BaseInspectionVisitor { + + @Override + public void visitLiteralExpression( + @NotNull PsiLiteralExpression expression) { + super.visitLiteralExpression(expression); + final PsiType type = expression.getType(); + if (type == null) { + return; + } + if (!type.equals(PsiType.CHAR)) { + return; + } + final String text = expression.getText(); + if (text == null) { + return; + } + if (text.equals(" ")) { + return; + } + if (ExpressionUtils.isDeclaredConstant(expression)) { + return; + } + if (NonNlsUtils.isNonNlsAnnotatedUse(expression)) { + return; + } + registerError(expression); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspectionBase.java new file mode 100644 index 000000000000..4ea6a4c7290f --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspectionBase.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.j2me; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.performance.InnerClassReferenceVisitor; +import org.jetbrains.annotations.NotNull; + +public class AnonymousInnerClassMayBeStaticInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "anonymous.inner.may.be.named.static.inner.class.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "anonymous.inner.may.be.named.static.inner.class.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new AnonymousInnerClassMayBeStaticVisitor(); + } + + private static class AnonymousInnerClassMayBeStaticVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (!(aClass instanceof PsiAnonymousClass)) { + return; + } + if (aClass instanceof PsiEnumConstantInitializer) { + return; + } + final PsiMember containingMember = + PsiTreeUtil.getParentOfType(aClass, PsiMember.class); + if (containingMember == null || + containingMember.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + final PsiAnonymousClass anAnonymousClass = + (PsiAnonymousClass)aClass; + final InnerClassReferenceVisitor visitor = + new InnerClassReferenceVisitor(anAnonymousClass); + anAnonymousClass.accept(visitor); + if (!visitor.canInnerClassBeStatic()) { + return; + } + registerClassError(aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionBase.java new file mode 100644 index 000000000000..d4714577f02f --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionBase.java @@ -0,0 +1,92 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.j2me; + +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class MethodCallInLoopConditionInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("method.call.in.loop.condition.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("method.call.in.loop.condition.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MethodCallInLoopConditionVisitor(); + } + + private static class MethodCallInLoopConditionVisitor extends BaseInspectionVisitor { + + @Override + public void visitForStatement(@NotNull PsiForStatement statement) { + super.visitForStatement(statement); + final PsiExpression condition = statement.getCondition(); + if (condition == null) { + return; + } + checkForMethodCalls(condition); + } + + @Override + public void visitWhileStatement(@NotNull PsiWhileStatement statement) { + super.visitWhileStatement(statement); + final PsiExpression condition = statement.getCondition(); + if (condition == null) { + return; + } + checkForMethodCalls(condition); + } + + @Override + public void visitDoWhileStatement(@NotNull PsiDoWhileStatement statement) { + super.visitDoWhileStatement(statement); + final PsiExpression condition = statement.getCondition(); + if (condition == null) { + return; + } + checkForMethodCalls(condition); + } + + private void checkForMethodCalls(PsiExpression condition) { + final PsiElementVisitor visitor = new JavaRecursiveElementVisitor() { + + @Override + public void visitMethodCallExpression( + @NotNull PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + registerMethodCallError(expression); + } + }; + condition.accept(visitor); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java index 92254308f431..1305663d7710 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java @@ -19,16 +19,18 @@ import com.intellij.analysis.AnalysisScope; import com.intellij.codeInspection.CommonProblemDescriptor; import com.intellij.codeInspection.GlobalInspectionContext; import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.reference.RefClass; import com.intellij.codeInspection.reference.RefEntity; import com.intellij.codeInspection.reference.RefPackage; import com.intellij.openapi.project.Project; -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiPackage; +import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseGlobalInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.BaseSharedLocalInspection; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -60,7 +62,7 @@ public class MissingPackageInfoInspection extends BaseGlobalInspection { final String packageName = refPackage.getQualifiedName(); final Project project = globalContext.getProject(); final PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage(packageName); - if (aPackage == null) { + if (hasPackageInfoFile(aPackage)) { return null; } final List children = refPackage.getChildren(); @@ -74,21 +76,77 @@ public class MissingPackageInfoInspection extends BaseGlobalInspection { if (!hasClasses) { return null; } + if (PsiUtil.isLanguageLevel5OrHigher(aPackage)) { + return new CommonProblemDescriptor[] { + manager.createProblemDescriptor(InspectionGadgetsBundle.message("missing.package.info.problem.descriptor", packageName))}; + } + else { + return new CommonProblemDescriptor[] { + manager.createProblemDescriptor(InspectionGadgetsBundle.message("missing.package.html.problem.descriptor", packageName))}; + } + } + + @Contract("null -> true") + static boolean hasPackageInfoFile(PsiPackage aPackage) { + if (aPackage == null) { + return true; + } final PsiDirectory[] directories = aPackage.getDirectories(); for (PsiDirectory directory : directories) { final boolean packageInfoFound = directory.findFile(PsiPackage.PACKAGE_INFO_FILE) != null; final boolean packageDotHtmlFound = directory.findFile("package.html") != null; if (packageInfoFound || packageDotHtmlFound) { - return null; + return true; } } - if (PsiUtil.isLanguageLevel5OrHigher(aPackage)) { - return new CommonProblemDescriptor[] { - manager.createProblemDescriptor(InspectionGadgetsBundle.message("missing.package.info.problem.descriptor", packageName))}; + return false; + } + + @Nullable + @Override + public LocalInspectionTool getSharedLocalInspectionTool() { + return new LocalMissingPackageInfoInspection(this); + } + + private static class LocalMissingPackageInfoInspection extends BaseSharedLocalInspection { + + public LocalMissingPackageInfoInspection(MissingPackageInfoInspection settingsDelegate) { + super(settingsDelegate); } - else { - return new CommonProblemDescriptor[] { - manager.createProblemDescriptor(InspectionGadgetsBundle.message("missing.package.html.problem.descriptor", packageName))}; + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + final PsiPackageStatement packageStatement = (PsiPackageStatement)infos[0]; + if (PsiUtil.isLanguageLevel5OrHigher(packageStatement)) { + return InspectionGadgetsBundle.message("missing.package.info.problem.descriptor", packageStatement.getPackageName()); + } + else { + return InspectionGadgetsBundle.message("missing.package.html.problem.descriptor", packageStatement.getPackageName()); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BaseInspectionVisitor() { + @Override + public void visitJavaFile(PsiJavaFile file) { + final PsiPackageStatement packageStatement = file.getPackageStatement(); + if (packageStatement == null) { + return; + } + final PsiJavaCodeReferenceElement packageReference = packageStatement.getPackageReference(); + final PsiElement target = packageReference.resolve(); + if (!(target instanceof PsiPackage)) { + return; + } + final PsiPackage aPackage = (PsiPackage)target; + if (hasPackageInfoFile(aPackage)) { + return; + } + registerError(packageReference, packageStatement); + } + }; } } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/PackageDotHtmlMayBePackageInfoInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/PackageDotHtmlMayBePackageInfoInspectionBase.java new file mode 100644 index 000000000000..019893e18461 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/PackageDotHtmlMayBePackageInfoInspectionBase.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.javadoc; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlFile; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class PackageDotHtmlMayBePackageInfoInspectionBase extends BaseInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("package.dot.html.may.be.package.info.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + if (((Boolean)infos[1]).booleanValue()) { + return InspectionGadgetsBundle.message("package.dot.html.may.be.package.info.exists.problem.descriptor"); + } + return InspectionGadgetsBundle.message("package.dot.html.may.be.package.info.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new PackageDotHtmlMayBePackageInfoVisitor(); + } + + private static class PackageDotHtmlMayBePackageInfoVisitor extends BaseInspectionVisitor { + + @Override + public void visitFile(PsiFile file) { + super.visitFile(file); + if (!(file instanceof XmlFile)) { + return; + } + @NonNls final String fileName = file.getName(); + if (!"package.html".equals(fileName)) { + return; + } + final PsiDirectory directory = file.getContainingDirectory(); + if (directory == null) { + return; + } + final String aPackage = getPackage(directory); + if (aPackage == null) { + return; + } + final boolean exists = directory.findFile("package-info.java") != null; + registerError(file, aPackage, Boolean.valueOf(exists)); + } + + public static String getPackage(@NotNull PsiDirectory directory) { + final VirtualFile virtualFile = directory.getVirtualFile(); + final Project project = directory.getProject(); + final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project); + final ProjectFileIndex fileIndex = projectRootManager.getFileIndex(); + return fileIndex.getPackageNameByDirectory(virtualFile); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/jdk/AssertAsNameInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/jdk/AssertAsNameInspectionBase.java new file mode 100644 index 000000000000..f4c8296789da --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/jdk/AssertAsNameInspectionBase.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.jdk; + +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class AssertAsNameInspectionBase extends BaseInspection { + @Override + @NotNull + public String getID() { + return "AssertAsIdentifier"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "use.assert.as.identifier.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "use.assert.as.identifier.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new AssertAsNameVisitor(); + } + + private static class AssertAsNameVisitor extends BaseInspectionVisitor { + + @Override + public void visitVariable(@NotNull PsiVariable variable) { + super.visitVariable(variable); + final String variableName = variable.getName(); + if (!PsiKeyword.ASSERT.equals(variableName)) { + return; + } + registerVariableError(variable); + } + + @Override + public void visitMethod(@NotNull PsiMethod method) { + super.visitMethod(method); + final String name = method.getName(); + if (!PsiKeyword.ASSERT.equals(name)) { + return; + } + registerMethodError(method); + } + + @Override + public void visitClass(@NotNull PsiClass aClass) { + //note: no call to super, to avoid drill-down + final String name = aClass.getName(); + if (!PsiKeyword.ASSERT.equals(name)) { + return; + } + final PsiTypeParameterList params = aClass.getTypeParameterList(); + if (params != null) { + params.accept(this); + } + registerClassError(aClass); + } + + @Override + public void visitTypeParameter(PsiTypeParameter parameter) { + super.visitTypeParameter(parameter); + final String name = parameter.getName(); + if (!PsiKeyword.ASSERT.equals(name)) { + return; + } + registerTypeParameterError(parameter); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/jdk/EnumAsNameInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/jdk/EnumAsNameInspectionBase.java new file mode 100644 index 000000000000..e24a29d6cb3a --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/jdk/EnumAsNameInspectionBase.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.jdk; + +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class EnumAsNameInspectionBase extends BaseInspection { + @Override + @NotNull + public String getID() { + return "EnumAsIdentifier"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "use.enum.as.identifier.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "use.enum.as.identifier.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new EnumAsNameVisitor(); + } + + private static class EnumAsNameVisitor extends BaseInspectionVisitor { + + @Override + public void visitVariable(@NotNull PsiVariable variable) { + super.visitVariable(variable); + final String variableName = variable.getName(); + if (!PsiKeyword.ENUM.equals(variableName)) { + return; + } + registerVariableError(variable); + } + + @Override + public void visitMethod(@NotNull PsiMethod method) { + super.visitMethod(method); + final String name = method.getName(); + if (!PsiKeyword.ENUM.equals(name)) { + return; + } + registerMethodError(method); + } + + @Override + public void visitClass(@NotNull PsiClass aClass) { + //note: no call to super, to avoid drill-down + final String name = aClass.getName(); + if (!PsiKeyword.ENUM.equals(name)) { + return; + } + final PsiTypeParameterList params = aClass.getTypeParameterList(); + if (params != null) { + params.accept(this); + } + registerClassError(aClass); + } + + @Override + public void visitTypeParameter(PsiTypeParameter parameter) { + super.visitTypeParameter(parameter); + final String name = parameter.getName(); + if (!PsiKeyword.ENUM.equals(name)) { + return; + } + registerTypeParameterError(parameter); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/BeforeClassOrAfterClassIsPublicStaticVoidNoArgInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/BeforeClassOrAfterClassIsPublicStaticVoidNoArgInspectionBase.java new file mode 100644 index 000000000000..69444ef1431f --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/BeforeClassOrAfterClassIsPublicStaticVoidNoArgInspectionBase.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiFormatUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TestUtils; +import org.jetbrains.annotations.NotNull; + +public class BeforeClassOrAfterClassIsPublicStaticVoidNoArgInspectionBase extends BaseInspection { + @Override + @NotNull + public String getID() { + return "BeforeOrAfterWithIncorrectSignature"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "before.class.or.after.class.is.public.static.void.no.arg.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "before.class.or.after.class.is.public.static.void.no.arg.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BeforeClassOrAfterClassIsPublicStaticVoidNoArgVisitor(); + } + + private static class BeforeClassOrAfterClassIsPublicStaticVoidNoArgVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + //note: no call to super; + if (!TestUtils.isJUnit4BeforeClassOrAfterClassMethod(method)) { + return; + } + final PsiType returnType = method.getReturnType(); + if (returnType == null) { + return; + } + final PsiClass targetClass = method.getContainingClass(); + if (targetClass == null) { + return; + } + + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() != 0 || + !returnType.equals(PsiType.VOID) || + !method.hasModifierProperty(PsiModifier.PUBLIC) || + !method.hasModifierProperty(PsiModifier.STATIC)) { + registerMethodError(method, "Change signature of \'" + + PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, + PsiFormatUtil.SHOW_NAME | + PsiFormatUtil.SHOW_MODIFIERS | + PsiFormatUtil.SHOW_PARAMETERS | + PsiFormatUtil.SHOW_TYPE, + PsiFormatUtil.SHOW_TYPE) + + "\' to \'public static void " + + method.getName() + + "()\'"); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnit4AnnotatedMethodInJUnit3TestCaseInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnit4AnnotatedMethodInJUnit3TestCaseInspectionBase.java new file mode 100644 index 000000000000..c527c7ee951c --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnit4AnnotatedMethodInJUnit3TestCaseInspectionBase.java @@ -0,0 +1,76 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TestUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +public class JUnit4AnnotatedMethodInJUnit3TestCaseInspectionBase extends BaseInspection { + protected static final String IGNORE = "org.junit.Ignore"; + + @Override + @Nls + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("junit4.test.method.in.class.extending.junit3.testcase.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + if (AnnotationUtil.isAnnotated((PsiMethod)infos[1], IGNORE, false)) { + return InspectionGadgetsBundle.message("ignore.test.method.in.class.extending.junit3.testcase.problem.descriptor"); + } + return InspectionGadgetsBundle.message("junit4.test.method.in.class.extending.junit3.testcase.problem.descriptor"); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new Junit4AnnotatedMethodInJunit3TestCaseVisitor(); + } + + private static class Junit4AnnotatedMethodInJunit3TestCaseVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(PsiMethod method) { + super.visitMethod(method); + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) { + return; + } + if (!TestUtils.isJUnitTestClass(containingClass)) { + return; + } + if (AnnotationUtil.isAnnotated(method, IGNORE, false) && method.getName().startsWith("test")) { + registerMethodError(method, containingClass, method); + } else if (TestUtils.isJUnit4TestMethod(method)) { + registerMethodError(method, containingClass, method); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitAbstractTestClassNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitAbstractTestClassNamingConventionInspectionBase.java new file mode 100644 index 000000000000..fce2f99b4b10 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitAbstractTestClassNamingConventionInspectionBase.java @@ -0,0 +1,115 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiTypeParameter; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.naming.ConventionInspection; +import org.jetbrains.annotations.NotNull; + +public class JUnitAbstractTestClassNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 12; + private static final int DEFAULT_MAX_LENGTH = 64; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "junit.abstract.test.class.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String className = (String)infos[0]; + if (className.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "junit.abstract.test.class.naming.convention.problem.descriptor.short"); + } + else if (className.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "junit.abstract.test.class.naming.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message( + "junit.abstract.test.class.naming.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[A-Z][A-Za-z\\d]*TestCase"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitElement(PsiElement element) { + if (!(element instanceof PsiClass)) { + super.visitElement(element); + return; + } + + PsiClass aClass = (PsiClass)element; + if (aClass.isInterface() || aClass.isEnum() || + aClass.isAnnotationType()) { + return; + } + if (aClass instanceof PsiTypeParameter) { + return; + } + if (!aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + return; + } + if (!InheritanceUtil.isInheritor(aClass, + "junit.framework.TestCase")) { + return; + } + final String name = aClass.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerClassError(aClass, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitTestClassNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitTestClassNamingConventionInspectionBase.java new file mode 100644 index 000000000000..cbbd14eab21b --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitTestClassNamingConventionInspectionBase.java @@ -0,0 +1,128 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.naming.ConventionInspection; +import com.siyeh.ig.psiutils.TestUtils; +import org.jetbrains.annotations.NotNull; + +public class JUnitTestClassNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 8; + private static final int DEFAULT_MAX_LENGTH = 64; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "junit.test.class.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String className = (String)infos[0]; + if (className.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "junit.test.class.naming.convention.problem.descriptor.short"); + } + else if (className.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "junit.test.class.naming.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message( + "junit.test.class.naming.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[A-Z][A-Za-z\\d]*Test"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + @Override + public void visitElement(PsiElement element) { + if (!(element instanceof PsiClass)) { + super.visitElement(element); + return; + } + + PsiClass aClass = (PsiClass)element; + if (aClass.isInterface() || aClass.isEnum() || + aClass.isAnnotationType()) { + return; + } + if (aClass instanceof PsiTypeParameter) { + return; + } + if (aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + return; + } + if (!InheritanceUtil.isInheritor(aClass, + "junit.framework.TestCase")) { + if (!hasJUnit4TestMethods(aClass)) { + return; + } + } + final String name = aClass.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerClassError(aClass, name); + } + + private boolean hasJUnit4TestMethods(@NotNull PsiClass aClass) { + //use this if this method turns out to have bad performance: + //if (!TestUtils.isTest(aClass)) { + // return false; + //} + final PsiMethod[] methods = aClass.getMethods(); + for (PsiMethod method : methods) { + if (TestUtils.isJUnit4TestMethod(method)) { + return true; + } + } + return false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisspelledSetUpInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisspelledSetUpInspectionBase.java new file mode 100644 index 000000000000..af3f874fca3b --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisspelledSetUpInspectionBase.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class MisspelledSetUpInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "misspelled.set.up.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "misspelled.set.up.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MisspelledSetUpVisitor(); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + private static class MisspelledSetUpVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + //note: no call to super + final PsiClass aClass = method.getContainingClass(); + @NonNls final String methodName = method.getName(); + if (!"setup".equals(methodName)) { + return; + } + if (aClass == null) { + return; + } + if (!InheritanceUtil.isInheritor(aClass, + "junit.framework.TestCase")) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisspelledTearDownInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisspelledTearDownInspectionBase.java new file mode 100644 index 000000000000..d6858bff4ec4 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/MisspelledTearDownInspectionBase.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class MisspelledTearDownInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "misspelled.tear.down.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "misspelled.tear.down.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MisspelledSetUpVisitor(); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + private static class MisspelledSetUpVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + // note: no call to super + @NonNls final String methodName = method.getName(); + if (!"teardown".equals(methodName)) { + return; + } + final PsiClass aClass = method.getContainingClass(); + if (aClass == null) { + return; + } + if (!InheritanceUtil.isInheritor(aClass, + "junit.framework.TestCase")) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/ParameterizedParametersStaticCollectionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/ParameterizedParametersStaticCollectionInspectionBase.java new file mode 100644 index 000000000000..b13dbc94ec4d --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/ParameterizedParametersStaticCollectionInspectionBase.java @@ -0,0 +1,129 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class ParameterizedParametersStaticCollectionInspectionBase extends BaseInspection { + protected static final String PARAMETERS_FQN = "org.junit.runners.Parameterized.Parameters"; + private static final String PARAMETERIZED_FQN = "org.junit.runners.Parameterized"; + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return infos.length > 0 + ? (String)infos[1] + : "Class #ref annotated @RunWith(Parameterized.class) lacks data provider"; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BaseInspectionVisitor() { + @Override + public void visitClass(PsiClass aClass) { + final PsiAnnotation annotation = AnnotationUtil.findAnnotation(aClass, "org.junit.runner.RunWith"); + if (annotation != null) { + for (PsiNameValuePair pair : annotation.getParameterList().getAttributes()) { + final PsiAnnotationMemberValue value = pair.getValue(); + if (value instanceof PsiClassObjectAccessExpression) { + final PsiTypeElement typeElement = ((PsiClassObjectAccessExpression)value).getOperand(); + if (typeElement.getType().getCanonicalText().equals(PARAMETERIZED_FQN)) { + List candidates = new ArrayList(); + for (PsiMethod method : aClass.getMethods()) { + PsiType returnType = method.getReturnType(); + final PsiClass returnTypeClass = PsiUtil.resolveClassInType(returnType); + final Project project = aClass.getProject(); + final PsiClass collectionsClass = + JavaPsiFacade.getInstance(project).findClass(Collection.class.getName(), GlobalSearchScope.allScope(project)); + if (AnnotationUtil.isAnnotated(method, PARAMETERS_FQN, false)) { + final PsiModifierList modifierList = method.getModifierList(); + boolean hasToFixSignature = false; + String message = "Make method \'" + method.getName() + "\' "; + String errorString = "Method \'#ref()\' should be "; + if (!modifierList.hasModifierProperty(PsiModifier.PUBLIC)) { + message += PsiModifier.PUBLIC + " "; + errorString += PsiModifier.PUBLIC + " "; + hasToFixSignature = true; + } + if (!modifierList.hasModifierProperty(PsiModifier.STATIC)) { + message += PsiModifier.STATIC; + errorString += PsiModifier.STATIC; + hasToFixSignature = true; + } + if (collectionsClass != null && + (returnTypeClass == null || !InheritanceUtil.isInheritorOrSelf(returnTypeClass, collectionsClass, true))) { + message += (hasToFixSignature ? " and" : "") + " return Collection"; + errorString += (hasToFixSignature ? " and" : "") + " return Collection"; + returnType = JavaPsiFacade.getElementFactory(project).createType(collectionsClass); + hasToFixSignature = true; + } + if (hasToFixSignature) { + candidates.add(new MethodCandidate(method, message, errorString, returnType)); + continue; + } + return; + } + } + if (candidates.isEmpty()) { + registerClassError(aClass); + } + else { + for (MethodCandidate candidate : candidates) { + registerMethodError(candidate.myMethod, candidate.myProblem, candidate.myErrorString, candidate.myReturnType); + } + } + } + } + } + } + } + }; + } + + @Override + @Nls + @NotNull + public String getDisplayName() { + return "@RunWith(Parameterized.class) without data provider"; + } + + private static class MethodCandidate { + PsiMethod myMethod; + String myProblem; + private final String myErrorString; + PsiType myReturnType; + + public MethodCandidate(PsiMethod method, String problem, String errorString, PsiType returnType) { + myMethod = method; + myProblem = problem; + myErrorString = errorString; + myReturnType = returnType; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestCaseInProductCodeInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestCaseInProductCodeInspectionBase.java new file mode 100644 index 000000000000..32f412b4184e --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestCaseInProductCodeInspectionBase.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TestUtils; +import org.jetbrains.annotations.NotNull; + +public class TestCaseInProductCodeInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "test.case.in.product.code.display.name"); + } + + @Override + @NotNull + public String getID() { + return "JUnitTestCaseInProductSource"; + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "test.case.in.product.code.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new TestCaseInProductCodeVisitor(); + } + + private static class TestCaseInProductCodeVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (TestUtils.isTest(aClass)) { + return; + } + if (!InheritanceUtil.isInheritor(aClass, + "junit.framework.TestCase")) { + return; + } + registerClassError(aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestMethodWithoutAssertionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestMethodWithoutAssertionInspectionBase.java new file mode 100644 index 000000000000..59710abc953b --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestMethodWithoutAssertionInspectionBase.java @@ -0,0 +1,255 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TestUtils; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +public class TestMethodWithoutAssertionInspectionBase extends BaseInspection { + protected final List methodNamePatterns = new ArrayList(); + protected final List classNames = new ArrayList(); + /** + * @noinspection PublicField + */ + @NonNls public String assertionMethods = + "org.junit.Assert,assert.*|fail.*," + + "junit.framework.Assert,assert.*|fail.*," + + "org.mockito.Mockito,verify.*," + + "org.junit.rules.ExpectedException,expect.*," + + "org.hamcrest.MatcherAssert,assertThat"; + @SuppressWarnings({"PublicField"}) + public boolean assertKeywordIsAssertion = false; + private Map patternCache = null; + + public TestMethodWithoutAssertionInspectionBase() { + parseString(assertionMethods, classNames, methodNamePatterns); + } + + @Override + @NotNull + public String getID() { + return "JUnitTestMethodWithNoAssertions"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("test.method.without.assertion.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("test.method.without.assertion.problem.descriptor"); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(assertionMethods, classNames, methodNamePatterns); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + assertionMethods = formatString(classNames, methodNamePatterns); + super.writeSettings(element); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new TestMethodWithoutAssertionVisitor(); + } + + private boolean methodNamesMatch(String methodName, String methodNamePattern) { + Pattern pattern; + if (patternCache != null) { + pattern = patternCache.get(methodNamePattern); + } + else { + patternCache = new HashMap(methodNamePatterns.size()); + pattern = null; + } + if (pattern == null) { + try { + pattern = Pattern.compile(methodNamePattern); + patternCache.put(methodNamePattern, pattern); + } + catch (PatternSyntaxException ignore) { + return false; + } + catch (NullPointerException ignore) { + return false; + } + } + if (pattern == null) { + return false; + } + final Matcher matcher = pattern.matcher(methodName); + return matcher.matches(); + } + + private class TestMethodWithoutAssertionVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + super.visitMethod(method); + if (!TestUtils.isJUnitTestMethod(method)) { + return; + } + if (hasExpectedExceptionAnnotation(method)) { + return; + } + if (containsAssertion(method)) { + return; + } + if (lastStatementIsCallToMethodWithAssertion(method)) { + return; + } + registerMethodError(method); + } + + private boolean lastStatementIsCallToMethodWithAssertion(PsiMethod method) { + final PsiCodeBlock body = method.getBody(); + if (body == null) { + return false; + } + final PsiStatement[] statements = body.getStatements(); + if (statements.length <= 0) { + return false; + } + final PsiStatement lastStatement = statements[0]; + if (!(lastStatement instanceof PsiExpressionStatement)) { + return false; + } + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)lastStatement; + final PsiExpression expression = expressionStatement.getExpression(); + if (!(expression instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + if (qualifierExpression != null && !(qualifierExpression instanceof PsiThisExpression)) { + return false; + } + final PsiMethod targetMethod = methodCallExpression.resolveMethod(); + return containsAssertion(targetMethod); + } + + private boolean containsAssertion(PsiElement element) { + if (element == null) { + return false; + } + final ContainsAssertionVisitor + visitor = new ContainsAssertionVisitor(); + element.accept(visitor); + return visitor.containsAssertion(); + } + + private boolean hasExpectedExceptionAnnotation(PsiMethod method) { + final PsiModifierList modifierList = method.getModifierList(); + final PsiAnnotation testAnnotation = modifierList.findAnnotation("org.junit.Test"); + if (testAnnotation == null) { + return false; + } + final PsiAnnotationParameterList parameterList = testAnnotation.getParameterList(); + final PsiNameValuePair[] nameValuePairs = parameterList.getAttributes(); + for (PsiNameValuePair nameValuePair : nameValuePairs) { + @NonNls final String parameterName = nameValuePair.getName(); + if ("expected".equals(parameterName)) { + return true; + } + } + return false; + } + } + + private class ContainsAssertionVisitor extends JavaRecursiveElementVisitor { + + private boolean containsAssertion = false; + + @Override + public void visitElement(@NotNull PsiElement element) { + if (!containsAssertion) { + super.visitElement(element); + } + } + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) { + if (containsAssertion) { + return; + } + super.visitMethodCallExpression(call); + final PsiReferenceExpression methodExpression = call.getMethodExpression(); + @NonNls final String methodName = methodExpression.getReferenceName(); + if (methodName == null) { + return; + } + final int methodNamesSize = methodNamePatterns.size(); + for (int i = 0; i < methodNamesSize; i++) { + final String pattern = methodNamePatterns.get(i); + if (!methodNamesMatch(methodName, pattern)) { + continue; + } + final PsiMethod method = call.resolveMethod(); + if (method == null || method.isConstructor()) { + continue; + } + final PsiClass aClass = method.getContainingClass(); + if (!InheritanceUtil.isInheritor(aClass, classNames.get(i))) { + continue; + } + containsAssertion = true; + break; + } + } + + @Override + public void visitAssertStatement(PsiAssertStatement statement) { + if (containsAssertion) { + return; + } + super.visitAssertStatement(statement); + if (!assertKeywordIsAssertion) { + return; + } + containsAssertion = true; + } + + public boolean containsAssertion() { + return containsAssertion; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/UseOfObsoleteAssertInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/UseOfObsoleteAssertInspection.java new file mode 100644 index 000000000000..59b168af1434 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/UseOfObsoleteAssertInspection.java @@ -0,0 +1,236 @@ +/* + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.junit; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class UseOfObsoleteAssertInspection extends BaseInspection { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("usage.of.obsolete.assert.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("use.of.obsolete.assert.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new ReplaceObsoleteAssertsFix(); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UseOfObsoleteAssertVisitor(); + } + + private static class UseOfObsoleteAssertVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + final Project project = expression.getProject(); + final Module module = ModuleUtilCore.findModuleForPsiElement(expression); + if (module == null) { + return; + } + final PsiClass newAssertClass = JavaPsiFacade.getInstance(project) + .findClass("org.junit.Assert", GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module)); + if (newAssertClass == null) { + return; + } + final PsiMethod psiMethod = expression.resolveMethod(); + if (psiMethod == null || !psiMethod.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + final PsiClass containingClass = psiMethod.getContainingClass(); + if (containingClass != null && Comparing.strEqual(containingClass.getQualifiedName(), "junit.framework.Assert")) { + registerMethodCallError(expression); + } + } + } + + private static class ReplaceObsoleteAssertsFix extends InspectionGadgetsFix { + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiElement psiElement = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class); + if (!(psiElement instanceof PsiMethodCallExpression)) { + return; + } + final PsiClass newAssertClass = + JavaPsiFacade.getInstance(project).findClass("org.junit.Assert", GlobalSearchScope.allScope(project)); + final PsiClass oldAssertClass = + JavaPsiFacade.getInstance(project).findClass("junit.framework.Assert", GlobalSearchScope.allScope(project)); + + if (newAssertClass == null) { + return; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)psiElement; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + final PsiElement usedImport = qualifierExpression instanceof PsiReferenceExpression ? + ((PsiReferenceExpression)qualifierExpression).advancedResolve(true).getCurrentFileResolveScope() : + methodExpression.advancedResolve(true).getCurrentFileResolveScope(); + final PsiMethod psiMethod = methodCallExpression.resolveMethod(); + + final boolean isImportUnused = isImportBecomeUnused(methodCallExpression, usedImport, psiMethod); + + PsiImportStaticStatement staticStatement = null; + if (qualifierExpression == null) { + staticStatement = staticallyImported(oldAssertClass, methodExpression); + } + + final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project); + if (staticStatement == null) { + methodExpression.setQualifierExpression(JavaPsiFacade.getElementFactory(project).createReferenceExpression(newAssertClass)); + + if (isImportUnused && usedImport instanceof PsiImportStatementBase) { + usedImport.delete(); + } + + styleManager.shortenClassReferences(methodExpression); + } + else { + if (isImportUnused) { + final PsiJavaCodeReferenceElement importReference = staticStatement.getImportReference(); + if (importReference != null) { + if (staticStatement.isOnDemand()) { + importReference.bindToElement(newAssertClass); + } + else { + final PsiElement importQExpression = importReference.getQualifier(); + if (importQExpression instanceof PsiReferenceExpression) { + ((PsiReferenceExpression)importQExpression).bindToElement(newAssertClass); + } + } + } + } + else { + methodExpression + .setQualifierExpression(JavaPsiFacade.getElementFactory(project).createReferenceExpression(newAssertClass)); + styleManager.shortenClassReferences(methodExpression); + } + } + /* + //refs can be optimized now but should we really? + if (isImportUnused) { + for (PsiReference reference : ReferencesSearch.search(newAssertClass, new LocalSearchScope(methodCallExpression.getContainingFile()))) { + final PsiElement element = reference.getElement(); + styleManager.shortenClassReferences(element); + } + }*/ + } + + private static boolean isImportBecomeUnused(final PsiMethodCallExpression methodCallExpression, + final PsiElement usedImport, + final PsiMethod psiMethod) { + final boolean[] proceed = new boolean[]{true}; + methodCallExpression.getContainingFile().accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitElement(PsiElement element) { + if (proceed[0]) { + super.visitElement(element); + } + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + if (expression == methodCallExpression) { + return; + } + final PsiMethod resolved = expression.resolveMethod(); + if (resolved == psiMethod) { + proceed[0] = false; + } + else { + final PsiElement resolveScope = + expression.getMethodExpression().advancedResolve(false).getCurrentFileResolveScope(); + if (resolveScope == usedImport) { + proceed[0] = false; + } + } + } + }); + return proceed[0]; + } + + @Nullable + private static PsiImportStaticStatement staticallyImported(PsiClass oldAssertClass, PsiReferenceExpression methodExpression) { + final String referenceName = methodExpression.getReferenceName(); + final PsiFile containingFile = methodExpression.getContainingFile(); + if (!(containingFile instanceof PsiJavaFile)) { + return null; + } + final PsiImportList importList = ((PsiJavaFile)containingFile).getImportList(); + if (importList == null) { + return null; + } + final PsiImportStaticStatement[] statements = importList.getImportStaticStatements(); + for (PsiImportStaticStatement statement : statements) { + if (oldAssertClass != statement.resolveTargetClass()) { + continue; + } + final String importRefName = statement.getReferenceName(); + final PsiJavaCodeReferenceElement importReference = statement.getImportReference(); + if (importReference == null) { + continue; + } + if (Comparing.strEqual(importRefName, referenceName)) { + final PsiElement qualifier = importReference.getQualifier(); + if (qualifier instanceof PsiJavaCodeReferenceElement) { + return statement; + } + } + else if (importRefName == null) { + return statement; + } + } + return null; + } + + @NotNull + @Override + public String getName() { + return InspectionGadgetsBundle.message("use.of.obsolete.assert.quickfix"); + } + + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/ClassWithMultipleLoggersInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/ClassWithMultipleLoggersInspectionBase.java new file mode 100644 index 000000000000..6478d2a97a9a --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/ClassWithMultipleLoggersInspectionBase.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.logging; + +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class ClassWithMultipleLoggersInspectionBase extends BaseInspection { + protected final List loggerNames = new ArrayList(); + /** + * @noinspection PublicField + */ + @NonNls + public String loggerNamesString = "java.util.logging.Logger" + ',' + + "org.slf4j.Logger" + ',' + + "org.apache.commons.logging.Log" + ',' + + "org.apache.log4j.Logger"; + + public ClassWithMultipleLoggersInspectionBase() { + parseString(loggerNamesString, loggerNames); + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("multiple.loggers.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "multiple.loggers.problem.descriptor"); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(loggerNamesString, loggerNames); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + loggerNamesString = formatString(loggerNames); + super.writeSettings(element); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ClassWithMultipleLoggersVisitor(); + } + + private class ClassWithMultipleLoggersVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + //no recursion to avoid drilldown + if (aClass.isInterface() || aClass.isEnum() || + aClass.isAnnotationType()) { + return; + } + if (aClass instanceof PsiTypeParameter) { + return; + } + if (aClass.getContainingClass() != null) { + return; + } + int numLoggers = 0; + final PsiField[] fields = aClass.getFields(); + for (PsiField field : fields) { + if (isLogger(field)) { + numLoggers++; + } + } + if (numLoggers <= 1) { + return; + } + registerClassError(aClass); + } + + private boolean isLogger(PsiVariable variable) { + final PsiType type = variable.getType(); + final String text = type.getCanonicalText(); + return loggerNames.contains(text); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/ClassWithoutLoggerInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/ClassWithoutLoggerInspectionBase.java new file mode 100644 index 000000000000..b1dad3eaadac --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/ClassWithoutLoggerInspectionBase.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.logging; + +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class ClassWithoutLoggerInspectionBase extends BaseInspection { + protected final List loggerNames = new ArrayList(); + /** + * @noinspection PublicField + */ + @NonNls + public String loggerNamesString = "java.util.logging.Logger" + ',' + + "org.slf4j.Logger" + ',' + + "org.apache.commons.logging.Log" + ',' + + "org.apache.log4j.Logger"; + /** + * @noinspection PublicField + */ + public boolean ignoreSuperLoggers = false; + + public ClassWithoutLoggerInspectionBase() { + parseString(loggerNamesString, loggerNames); + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("no.logger.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("no.logger.problem.descriptor"); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(loggerNamesString, loggerNames); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + loggerNamesString = formatString(loggerNames); + super.writeSettings(element); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ClassWithoutLoggerVisitor(); + } + + private class ClassWithoutLoggerVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + //no recursion to avoid drilldown + if (aClass.isInterface() || aClass.isEnum() || + aClass.isAnnotationType()) { + return; + } + if (aClass instanceof PsiTypeParameter || + aClass instanceof PsiAnonymousClass) { + return; + } + if (aClass.getContainingClass() != null) { + return; + } + final PsiField[] fields; + if (ignoreSuperLoggers) { + fields = aClass.getAllFields(); + } + else { + fields = aClass.getFields(); + } + for (PsiField field : fields) { + if (isLogger(field)) { + if (PsiUtil.isAccessible(field, aClass, aClass)) { + return; + } + } + } + registerClassError(aClass); + } + + private boolean isLogger(PsiVariable variable) { + final PsiType type = variable.getType(); + final String text = type.getCanonicalText(); + return loggerNames.contains(text); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LogStatementGuardedByLogConditionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LogStatementGuardedByLogConditionInspectionBase.java new file mode 100644 index 000000000000..0413d835f139 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LogStatementGuardedByLogConditionInspectionBase.java @@ -0,0 +1,273 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.logging; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.TypeUtils; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class LogStatementGuardedByLogConditionInspectionBase extends BaseInspection { + final List logMethodNameList = new ArrayList(); + final List logConditionMethodNameList = new ArrayList(); + @SuppressWarnings({"PublicField"}) + public String loggerClassName = "java.util.logging.Logger"; + @NonNls + @SuppressWarnings({"PublicField"}) + public String loggerMethodAndconditionMethodNames = + "fine,isLoggable(java.util.logging.Level.FINE)," + + "finer,isLoggable(java.util.logging.Level.FINER)," + + "finest,isLoggable(java.util.logging.Level.FINEST)"; + @SuppressWarnings("PublicField") + public boolean flagAllUnguarded = false; + + public LogStatementGuardedByLogConditionInspectionBase() { + parseString(loggerMethodAndconditionMethodNames, logMethodNameList, logConditionMethodNameList); + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("log.statement.guarded.by.log.condition.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("log.statement.guarded.by.log.condition.problem.descriptor"); + } + + @Override + @Nullable + protected InspectionGadgetsFix buildFix(Object... infos) { + return new LogStatementGuardedByLogConditionFix(); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new LogStatementGuardedByLogConditionVisitor(); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(loggerMethodAndconditionMethodNames, logMethodNameList, logConditionMethodNameList); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + loggerMethodAndconditionMethodNames = formatString(logMethodNameList, logConditionMethodNameList); + super.writeSettings(element); + } + + private class LogStatementGuardedByLogConditionFix extends InspectionGadgetsFix { + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("log.statement.guarded.by.log.condition.quickfix"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement(); + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element.getParent().getParent(); + final PsiStatement statement = PsiTreeUtil.getParentOfType(methodCallExpression, PsiStatement.class); + if (statement == null) { + return; + } + final List logStatements = new ArrayList(); + logStatements.add(statement); + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String referenceName = methodExpression.getReferenceName(); + if (referenceName == null) { + return; + } + PsiStatement previousStatement = PsiTreeUtil.getPrevSiblingOfType(statement, PsiStatement.class); + while (previousStatement != null && isSameLogMethodCall(previousStatement, referenceName)) { + logStatements.add(0, previousStatement); + previousStatement = PsiTreeUtil.getPrevSiblingOfType(previousStatement, PsiStatement.class); + } + PsiStatement nextStatement = PsiTreeUtil.getNextSiblingOfType(statement, PsiStatement.class); + while (nextStatement != null && isSameLogMethodCall(nextStatement, referenceName)) { + logStatements.add(nextStatement); + nextStatement = PsiTreeUtil.getNextSiblingOfType(nextStatement, PsiStatement.class); + } + final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (qualifier == null) { + return; + } + final int index = logMethodNameList.indexOf(referenceName); + final String conditionMethodText = logConditionMethodNameList.get(index); + @NonNls final String ifStatementText = "if (" + qualifier.getText() + '.' + conditionMethodText + ") {}"; + final PsiIfStatement ifStatement = (PsiIfStatement)factory.createStatementFromText(ifStatementText, statement); + final PsiBlockStatement blockStatement = (PsiBlockStatement)ifStatement.getThenBranch(); + if (blockStatement == null) { + return; + } + final PsiCodeBlock codeBlock = blockStatement.getCodeBlock(); + for (PsiStatement logStatement : logStatements) { + codeBlock.add(logStatement); + } + final PsiStatement firstStatement = logStatements.get(0); + final PsiElement parent = firstStatement.getParent(); + final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); + if (parent instanceof PsiIfStatement && ((PsiIfStatement)parent).getElseBranch() != null) { + final PsiBlockStatement newBlockStatement = (PsiBlockStatement)factory.createStatementFromText("{}", statement); + newBlockStatement.getCodeBlock().add(ifStatement); + final PsiElement result = firstStatement.replace(newBlockStatement); + codeStyleManager.shortenClassReferences(result); + return; + } + final PsiElement result = parent.addBefore(ifStatement, firstStatement); + codeStyleManager.shortenClassReferences(result); + for (PsiStatement logStatement : logStatements) { + logStatement.delete(); + } + } + + private boolean isSameLogMethodCall(PsiStatement statement, @NotNull String methodName) { + if (statement == null) { + return false; + } + if (!(statement instanceof PsiExpressionStatement)) { + return false; + } + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)statement; + final PsiExpression expression = expressionStatement.getExpression(); + if (!(expression instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String referenceName = methodExpression.getReferenceName(); + if (!methodName.equals(referenceName)) { + return false; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + return qualifier != null && TypeUtils.expressionHasTypeOrSubtype(qualifier, loggerClassName); + } + } + + private class LogStatementGuardedByLogConditionVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final String referenceName = methodExpression.getReferenceName(); + if (!logMethodNameList.contains(referenceName)) { + return; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (qualifier == null) { + return; + } + if (!TypeUtils.expressionHasTypeOrSubtype(qualifier, loggerClassName)) { + return; + } + if (isSurroundedByLogGuard(expression, referenceName)) { + return; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length == 0) { + return; + } + if (!flagAllUnguarded) { + boolean constant = true; + for (PsiExpression argument : arguments) { + if (!PsiUtil.isConstantExpression(argument)) { + constant = false; + break; + } + } + if (constant) { + return; + } + } + registerMethodCallError(expression); + } + + private boolean isSurroundedByLogGuard(PsiElement element, String logMethodName) { + while (true) { + final PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(element, PsiIfStatement.class); + if (ifStatement == null) { + return false; + } + final PsiExpression condition = ifStatement.getCondition(); + if (isLogGuardCheck(condition, logMethodName)) { + return true; + } + element = ifStatement; + } + } + + private boolean isLogGuardCheck(@Nullable PsiExpression expression, String logMethodName) { + if (expression instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (qualifier == null) { + return false; + } + if (!TypeUtils.expressionHasTypeOrSubtype(qualifier, loggerClassName)) { + return false; + } + final String referenceName = methodExpression.getReferenceName(); + if (referenceName == null) { + return false; + } + final int index = logMethodNameList.indexOf(logMethodName); + final String conditionName = logConditionMethodNameList.get(index); + return conditionName.startsWith(referenceName); + } + else if (expression instanceof PsiPolyadicExpression) { + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; + final PsiExpression[] operands = polyadicExpression.getOperands(); + for (PsiExpression operand : operands) { + if (isLogGuardCheck(operand, logMethodName)) { + return true; + } + } + } + return false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LoggerInitializedWithForeignClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LoggerInitializedWithForeignClassInspectionBase.java new file mode 100644 index 000000000000..5a70933d865b --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LoggerInitializedWithForeignClassInspectionBase.java @@ -0,0 +1,202 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.logging; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class LoggerInitializedWithForeignClassInspectionBase extends BaseInspection { + @NonNls private static final String DEFAULT_LOGGER_CLASS_NAMES = + "org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger"; + @NonNls private static final String DEFAULT_FACTORY_METHOD_NAMES = "getLogger,getLogger,getLog,getLogger"; + protected final List loggerFactoryClassNames = new ArrayList(); + protected final List loggerFactoryMethodNames = new ArrayList(); + @SuppressWarnings({"PublicField"}) + public String loggerClassName = DEFAULT_LOGGER_CLASS_NAMES; + @SuppressWarnings({"PublicField"}) + public String loggerFactoryMethodName = DEFAULT_FACTORY_METHOD_NAMES; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("logger.initialized.with.foreign.class.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("logger.initialized.with.foreign.class.problem.descriptor"); + } + + @Override + @Nullable + protected InspectionGadgetsFix buildFix(Object... infos) { + return new LoggerInitializedWithForeignClassFix((String)infos[0]); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new LoggerInitializedWithForeignClassVisitor(); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(loggerClassName, loggerFactoryClassNames); + parseString(loggerFactoryMethodName, loggerFactoryMethodNames); + if (loggerFactoryClassNames.size() != loggerFactoryMethodNames.size()) { + parseString(DEFAULT_LOGGER_CLASS_NAMES, loggerFactoryClassNames); + parseString(DEFAULT_FACTORY_METHOD_NAMES, loggerFactoryMethodNames); + } + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + loggerClassName = formatString(loggerFactoryClassNames); + loggerFactoryMethodName = formatString(loggerFactoryMethodNames); + super.writeSettings(element); + } + + private static class LoggerInitializedWithForeignClassFix extends InspectionGadgetsFix { + + private final String newClassName; + + private LoggerInitializedWithForeignClassFix(String newClassName) { + this.newClassName = newClassName; + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("logger.initialized.with.foreign.class.quickfix", newClassName); + } + + @NotNull + @Override + public String getFamilyName() { + return "Replace foreign class"; + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + if (!(element instanceof PsiClassObjectAccessExpression)) { + return; + } + final PsiClassObjectAccessExpression classObjectAccessExpression = (PsiClassObjectAccessExpression)element; + replaceExpression(classObjectAccessExpression, newClassName + ".class"); + } + } + + private class LoggerInitializedWithForeignClassVisitor extends BaseInspectionVisitor { + + @Override + public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) { + super.visitClassObjectAccessExpression(expression); + PsiElement parent = expression.getParent(); + if (parent instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)parent; + if (!expression.equals(referenceExpression.getQualifierExpression())) { + return; + } + @NonNls final String name = referenceExpression.getReferenceName(); + if (!"getName".equals(name)) { + return; + } + final PsiElement grandParent = referenceExpression.getParent(); + if (!(grandParent instanceof PsiMethodCallExpression)) { + return; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent; + final PsiExpressionList list = methodCallExpression.getArgumentList(); + if (list.getExpressions().length != 0) { + return; + } + parent = methodCallExpression.getParent(); + } + if (!(parent instanceof PsiExpressionList)) { + return; + } + final PsiElement grandParent = parent.getParent(); + if (!(grandParent instanceof PsiMethodCallExpression)) { + return; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent; + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + final PsiExpression[] expressions = argumentList.getExpressions(); + if (expressions.length != 1) { + return; + } + final PsiClass containingClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class); + if (containingClass == null) { + return; + } + final String containingClassName = containingClass.getName(); + if (containingClassName == null) { + return; + } + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass aClass = method.getContainingClass(); + if (aClass == null) { + return; + } + final String className = aClass.getQualifiedName(); + final int index = loggerFactoryClassNames.indexOf(className); + if (index < 0) { + return; + } + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String referenceName = methodExpression.getReferenceName(); + final String loggerFactoryMethodName = loggerFactoryMethodNames.get(index); + if (!loggerFactoryMethodName.equals(referenceName)) { + return; + } + final PsiTypeElement operand = expression.getOperand(); + final PsiType type = operand.getType(); + if (!(type instanceof PsiClassType)) { + return; + } + final PsiClassType classType = (PsiClassType)type; + final PsiClass initializerClass = classType.resolve(); + if (initializerClass == null) { + return; + } + if (containingClass.equals(initializerClass)) { + return; + } + registerError(expression, containingClassName); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LoggingConditionDisagreesWithLogStatementInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LoggingConditionDisagreesWithLogStatementInspection.java new file mode 100644 index 000000000000..10d7a75a2b61 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/LoggingConditionDisagreesWithLogStatementInspection.java @@ -0,0 +1,355 @@ +/* + * Copyright 2008-2012 Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.logging; + +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class LoggingConditionDisagreesWithLogStatementInspection extends BaseInspection { + + private static final Set loggingLevels = new HashSet(Arrays.asList( + "debug", "error", "fatal", "info", "trace", "warn", "severe", "warning", "info", "config", "fine", "finer", "finest")); + + private static final Map problemCheckers = new HashMap(); + + static { + register(new Log4jProblemChecker()); + register(new CommonsLoggingProblemChecker()); + register(new JavaUtilLoggingProblemChecker()); + register(new Slf4jProblemChecker()); + } + + private static void register(LoggingProblemChecker problemChecker) { + for (String name : problemChecker.getClassNames()) { + problemCheckers.put(name, problemChecker); + } + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("logging.condition.disagrees.with.log.statement.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("logging.condition.disagrees.with.log.statement.problem.descriptor", infos[0]); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new LoggingConditionDisagreesWithLogStatementVisitor(); + } + + private static class LoggingConditionDisagreesWithLogStatementVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final String referenceName = methodExpression.getReferenceName(); + if (referenceName == null) { + return; + } + final String loggingLevel; + if (!loggingLevels.contains(referenceName)) { + if (!"log".equals(referenceName)) { + return; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length < 2) { + return; + } + final PsiExpression argument = arguments[0]; + loggingLevel = JavaUtilLoggingProblemChecker.getLoggingLevelFromArgument(argument); + if (loggingLevel == null) { + return; + } + } + else { + loggingLevel = referenceName; + } + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) { + return; + } + final PsiElement parent = expression.getParent(); + if (!(parent instanceof PsiExpressionStatement)) { + return; + } + final PsiElement grandParent = parent.getParent(); + final PsiIfStatement ifStatement; + if (grandParent instanceof PsiCodeBlock) { + final PsiElement greatGrandParent = grandParent.getParent(); + if (!(greatGrandParent instanceof PsiBlockStatement)) { + return; + } + final PsiElement greatGreatGrandParent = greatGrandParent.getParent(); + if (!(greatGreatGrandParent instanceof PsiIfStatement)) { + return; + } + ifStatement = (PsiIfStatement)greatGreatGrandParent; + } + else if (grandParent instanceof PsiIfStatement) { + ifStatement = (PsiIfStatement)grandParent; + } + else { + return; + } + PsiExpression condition = ifStatement.getCondition(); + if (condition instanceof PsiMethodCallExpression) { + final PsiStatement thenBranch = ifStatement.getThenBranch(); + if (!PsiTreeUtil.isAncestor(thenBranch, expression, false)) { + return; + } + } + else if (condition instanceof PsiPrefixExpression) { + final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)condition; + if (!JavaTokenType.EXCL.equals(prefixExpression.getOperationTokenType())) { + return; + } + final PsiStatement elseBranch = ifStatement.getElseBranch(); + if (!PsiTreeUtil.isAncestor(elseBranch, expression, false)) { + return; + } + condition = prefixExpression.getOperand(); + if (!(condition instanceof PsiMethodCallExpression)) { + return; + } + } + else { + return; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (!(qualifier instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + if (target == null) { + return; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)condition; + final PsiReferenceExpression conditionMethodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression conditionQualifier = conditionMethodExpression.getQualifierExpression(); + if (!(conditionQualifier instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression conditionReferenceExpression = (PsiReferenceExpression)conditionQualifier; + final PsiElement conditionTarget = conditionReferenceExpression.resolve(); + if (!target.equals(conditionTarget)) { + return; + } + final String qualifiedName = containingClass.getQualifiedName(); + final LoggingProblemChecker problemChecker = problemCheckers.get(qualifiedName); + if (problemChecker == null || !problemChecker.hasLoggingProblem(loggingLevel, methodCallExpression)) { + return; + } + registerMethodCallError(methodCallExpression, loggingLevel); + } + } + + interface LoggingProblemChecker { + + String[] getClassNames(); + + boolean hasLoggingProblem(String priority, PsiMethodCallExpression methodCallExpression); + } + + private static class JavaUtilLoggingProblemChecker implements LoggingProblemChecker { + + @Override + public String[] getClassNames() { + return new String[]{"java.util.logging.Logger"}; + } + + @Override + public boolean hasLoggingProblem(String priority, PsiMethodCallExpression methodCallExpression) { + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if (!"isLoggable".equals(methodName)) { + return false; + } + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 1) { + return false; + } + final PsiExpression argument = arguments[0]; + final String loggingLevel = getLoggingLevelFromArgument(argument); + if (loggingLevel == null) { + return false; + } + return !loggingLevel.equals(priority); + } + + @Nullable + public static String getLoggingLevelFromArgument(PsiExpression argument) { + if (!(argument instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression argumentReference = (PsiReferenceExpression)argument; + final PsiType type = argument.getType(); + if (!(type instanceof PsiClassType)) { + return null; + } + final PsiClassType classType = (PsiClassType)type; + final PsiClass aClass = classType.resolve(); + if (aClass == null) { + return null; + } + final String qName = aClass.getQualifiedName(); + if (!"java.util.logging.Level".equals(qName)) { + return null; + } + final PsiElement argumentTarget = argumentReference.resolve(); + if (!(argumentTarget instanceof PsiField)) { + return null; + } + final PsiField field = (PsiField)argumentTarget; + return field.getName().toLowerCase(); + } + } + + private static class CommonsLoggingProblemChecker implements LoggingProblemChecker { + + @Override + public String[] getClassNames() { + return new String[]{"org.apache.commons.logging.Log"}; + } + + @Override + public boolean hasLoggingProblem(String priority, PsiMethodCallExpression methodCallExpression) { + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if ("isTraceEnabled".equals(methodName)) { + return !priority.equals("trace"); + } + else if ("isDebugEnabled".equals(methodName)) { + return !priority.equals("debug"); + } + else if ("isInfoEnabled".equals(methodName)) { + return !priority.equals("info"); + } + else if ("isWarnEnabled".equals(methodName)) { + return !priority.equals("warn"); + } + else if ("isErrorEnabled".equals(methodName)) { + return !priority.equals("error"); + } + else if ("isFatalEnabled".equals(methodName)) { + return !priority.equals("fatal"); + } + return false; + } + } + + private static class Slf4jProblemChecker implements LoggingProblemChecker { + @Override + public String[] getClassNames() { + return new String[]{"org.slf4j.Logger"}; + } + + @Override + public boolean hasLoggingProblem(String priority, PsiMethodCallExpression methodCallExpression) { + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if ("isTraceEnabled".equals(methodName)) { + return !"trace".equals(priority); + } + else if ("isDebugEnabled".equals(methodName)) { + return !"debug".equals(priority); + } + else if ("isInfoEnabled".equals(methodName)) { + return !"info".equals(priority); + } + else if ("isWarnEnabled".equals(methodName)) { + return !"warn".equals(priority); + } + else if ("isErrorEnabled".equals(methodName)) { + return !"error".equals(priority); + } + return false; + } + } + + private static class Log4jProblemChecker implements LoggingProblemChecker { + + @Override + public String[] getClassNames() { + return new String[]{"org.apache.log4j.Logger", "org.apache.log4j.Category"}; + } + + @Override + public boolean hasLoggingProblem(String priority, PsiMethodCallExpression methodCallExpression) { + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + String enabledFor = null; + if ("isDebugEnabled".equals(methodName)) { + enabledFor = "debug"; + } + else if ("isInfoEnabled".equals(methodName)) { + enabledFor = "info"; + } + else if ("isTraceEnabled".equals(methodName)) { + enabledFor = "trace"; + } + else if ("isEnabledFor".equals(methodName)) { + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + for (PsiExpression argument : arguments) { + if (!(argument instanceof PsiReferenceExpression)) { + continue; + } + final PsiReferenceExpression argumentReference = (PsiReferenceExpression)argument; + final PsiType type = argument.getType(); + if (!(type instanceof PsiClassType)) { + continue; + } + final PsiClassType classType = (PsiClassType)type; + final PsiClass aClass = classType.resolve(); + if (!InheritanceUtil.isInheritor(aClass, "org.apache.log4j.Priority")) { + continue; + } + final PsiElement argumentTarget = argumentReference.resolve(); + if (!(argumentTarget instanceof PsiField)) { + continue; + } + final PsiField field = (PsiField)argumentTarget; + enabledFor = field.getName().toLowerCase(); + } + if (enabledFor == null) { + return false; + } + } + return !priority.equals(enabledFor); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/NonStaticFinalLoggerInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/NonStaticFinalLoggerInspectionBase.java new file mode 100644 index 000000000000..78d1aa9c858a --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/NonStaticFinalLoggerInspectionBase.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.logging; + +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.fixes.MakeFieldStaticFinalFix; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class NonStaticFinalLoggerInspectionBase extends BaseInspection { + protected final List loggerClassNames = new ArrayList(); + @SuppressWarnings("PublicField") + public String loggerClassName = "java.util.logging.Logger" + ',' + + "org.slf4j.Logger" + ',' + + "org.apache.commons.logging.Log" + ',' + + "org.apache.log4j.Logger"; + + @Override + @NotNull + public String getID() { + return "NonConstantLogger"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("non.constant.logger.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("non.constant.logger.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final PsiField field = (PsiField)infos[0]; + return MakeFieldStaticFinalFix.buildFixUnconditional(field); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(loggerClassName, loggerClassNames); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + loggerClassName = formatString(loggerClassNames); + super.writeSettings(element); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NonStaticFinalLoggerVisitor(); + } + + private class NonStaticFinalLoggerVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (aClass.isInterface() || aClass.isEnum() || aClass.isAnnotationType()) { + return; + } + if (aClass instanceof PsiTypeParameter) { + return; + } + if (aClass.getContainingClass() != null) { + return; + } + final PsiField[] fields = aClass.getFields(); + for (final PsiField field : fields) { + if (!isLogger(field)) { + continue; + } + if (field.hasModifierProperty(PsiModifier.STATIC) && field.hasModifierProperty(PsiModifier.FINAL)) { + continue; + } + registerFieldError(field, field); + } + } + + private boolean isLogger(PsiVariable variable) { + final PsiType type = variable.getType(); + final String text = type.getCanonicalText(); + return loggerClassNames.contains(text); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/PlaceholderCountMatchesArgumentCountInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/PlaceholderCountMatchesArgumentCountInspection.java new file mode 100644 index 000000000000..0a6ef31fcc7e --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/PlaceholderCountMatchesArgumentCountInspection.java @@ -0,0 +1,128 @@ +/* + * Copyright 2013 Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.logging; + +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.util.containers.ContainerUtilRt; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public class PlaceholderCountMatchesArgumentCountInspection extends BaseInspection { + + @NonNls + private static final Set loggingMethodNames = ContainerUtilRt.newHashSet("trace", "debug", "info", "warn", "error"); + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("placeholder.count.matches.argument.count.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + final int argumentCount = ((Integer)infos[0]).intValue(); + final int placeholderCount = ((Integer)infos[1]).intValue(); + if (argumentCount > placeholderCount) { + return InspectionGadgetsBundle.message("placeholder.count.matches.argument.count.more.problem.descriptor", + argumentCount, placeholderCount); + } + else { + return InspectionGadgetsBundle.message("placeholder.count.matches.argument.count.fewer.problem.descriptor", + argumentCount, placeholderCount); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new PlaceholderCountMatchesArgumentCountVisitor(); + } + + private static class PlaceholderCountMatchesArgumentCountVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final String name = methodExpression.getReferenceName(); + if (!loggingMethodNames.contains(name)) { + return; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length == 0) { + return; + } + final PsiExpression firstArgument = arguments[0]; + final int placeholderCount; + final int argumentCount; + if (InheritanceUtil.isInheritor(firstArgument.getType(), "org.slf4j.Marker")) { + if (arguments.length < 2) { + return; + } + final PsiExpression secondArgument = arguments[1]; + if (!ExpressionUtils.hasStringType(secondArgument)) { + return; + } + final String value = (String)ExpressionUtils.computeConstantExpression(secondArgument); + if (value == null) { + return; + } + placeholderCount = countPlaceholders(value); + argumentCount = hasThrowableType(arguments[arguments.length - 1]) ? arguments.length - 3 : arguments.length - 2; + } + else if (ExpressionUtils.hasStringType(firstArgument)) { + final String value = (String)ExpressionUtils.computeConstantExpression(firstArgument); + if (value == null) { + return; + } + placeholderCount = countPlaceholders(value); + argumentCount = hasThrowableType(arguments[arguments.length - 1]) ? arguments.length - 2 : arguments.length - 1; + } else { + return; + } + if (placeholderCount == argumentCount) { + return; + } + registerMethodCallError(expression, argumentCount, placeholderCount); + } + + private static boolean hasThrowableType(PsiExpression lastArgument) { + return InheritanceUtil.isInheritor(lastArgument.getType(), "java.lang.Throwable"); + } + + public static int countPlaceholders(String value) { + int count = 0; + int index = value.indexOf("{}"); + while (index >= 0) { + if (index <= 0 || value.charAt(index - 1) != '\\') { + count++; + } + index = value.indexOf("{}", index + 1); + } + return count; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/PublicMethodWithoutLoggingInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/PublicMethodWithoutLoggingInspectionBase.java new file mode 100644 index 000000000000..0752e8678aa4 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/PublicMethodWithoutLoggingInspectionBase.java @@ -0,0 +1,142 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.logging; + +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.*; +import com.intellij.psi.util.PropertyUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class PublicMethodWithoutLoggingInspectionBase extends BaseInspection { + protected final List loggerClassNames = new ArrayList(); + @SuppressWarnings("PublicField") + public String loggerClassName = "java.util.logging.Logger" + ',' + + "org.slf4j.Logger" + ',' + + "org.apache.commons.logging.Log" + ',' + + "org.apache.log4j.Logger"; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "public.method.without.logging.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("public.method.without.logging.problem.descriptor"); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(loggerClassName, loggerClassNames); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + loggerClassName = formatString(loggerClassNames); + super.writeSettings(element); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new PublicMethodWithoutLoggingVisitor(); + } + + private class PublicMethodWithoutLoggingVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + //no drilldown + if (method.getNameIdentifier() == null) { + return; + } + if (!method.hasModifierProperty(PsiModifier.PUBLIC)) { + return; + } + final PsiCodeBlock body = method.getBody(); + if (body == null) { + return; + } + if (method.isConstructor()) { + return; + } + if (PropertyUtil.isSimpleGetter(method) || PropertyUtil.isSimpleSetter(method)) { + return; + } + if (containsLoggingCall(body)) { + return; + } + registerMethodError(method); + } + + private boolean containsLoggingCall(PsiCodeBlock block) { + ContainsLoggingCallVisitor visitor = new ContainsLoggingCallVisitor(); + block.accept(visitor); + return visitor.containsLoggingCall(); + } + } + + private class ContainsLoggingCallVisitor extends JavaRecursiveElementVisitor { + + private boolean containsLoggingCall = false; + + @Override + public void visitElement(@NotNull PsiElement element) { + if (containsLoggingCall) { + return; + } + super.visitElement(element); + } + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { + if (containsLoggingCall) { + return; + } + super.visitMethodCallExpression(expression); + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) { + return; + } + final String containingClassName = containingClass.getQualifiedName(); + if (containingClassName == null) { + return; + } + if (loggerClassNames.contains(containingClassName)) { + containsLoggingCall = true; + } + } + + public boolean containsLoggingCall() { + return containsLoggingCall; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/StringConcatenationArgumentToLogCallInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/StringConcatenationArgumentToLogCallInspection.java new file mode 100644 index 000000000000..40925af80232 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/StringConcatenationArgumentToLogCallInspection.java @@ -0,0 +1,303 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.logging; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import com.siyeh.ig.psiutils.TypeUtils; +import gnu.trove.THashSet; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * @author Bas Leijdekkers + */ +public class StringConcatenationArgumentToLogCallInspection extends BaseInspection { + + @NonNls + private static final Set logNames = new THashSet(); + static { + logNames.add("trace"); + logNames.add("debug"); + logNames.add("info"); + logNames.add("warn"); + logNames.add("error"); + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("string.concatenation.argument.to.log.call.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("string.concatenation.argument.to.log.call.problem.descriptor"); + } + + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + if (!StringConcatenationArgumentToLogCallFix.isAvailable((PsiExpression)infos[0])) { + return null; + } + return new StringConcatenationArgumentToLogCallFix(); + } + + private static class StringConcatenationArgumentToLogCallFix extends InspectionGadgetsFix { + + public StringConcatenationArgumentToLogCallFix() {} + + @NotNull + @Override + public String getName() { + return InspectionGadgetsBundle.message("string.concatenation.argument.to.log.call.quickfix"); + } + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + final PsiElement grandParent = element.getParent().getParent(); + if (!(grandParent instanceof PsiMethodCallExpression)) { + return; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent; + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length == 0) { + return; + } + @NonNls final StringBuilder newMethodCall = new StringBuilder(methodCallExpression.getMethodExpression().getText()); + newMethodCall.append('('); + PsiExpression argument = arguments[0]; + int usedArguments; + if (!(argument instanceof PsiPolyadicExpression)) { + if (!TypeUtils.expressionHasTypeOrSubtype(argument, "org.slf4j.Marker") || arguments.length < 2) { + return; + } + newMethodCall.append(argument.getText()).append(','); + argument = arguments[1]; + usedArguments = 2; + if (!(argument instanceof PsiPolyadicExpression)) { + return; + } + } + else { + usedArguments = 1; + } + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)argument; + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method == null) { + return; + } + final String methodName = method.getName(); + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) { + return; + } + final PsiMethod[] methods = containingClass.findMethodsByName(methodName, false); + boolean varArgs = false; + for (PsiMethod otherMethod : methods) { + if (otherMethod.isVarArgs()) { + varArgs = true; + break; + } + } + final List newArguments = new ArrayList(); + final PsiExpression[] operands = polyadicExpression.getOperands(); + boolean addPlus = false; + boolean inStringLiteral = false; + for (PsiExpression operand : operands) { + if (ExpressionUtils.isEvaluatedAtCompileTime(operand)) { + if (ExpressionUtils.hasStringType(operand) && operand instanceof PsiLiteralExpression) { + final String text = operand.getText(); + final int count = StringUtil.getOccurrenceCount(text, "{}"); + for (int i = 0; i < count && usedArguments + i < arguments.length; i++) { + newArguments.add(ParenthesesUtils.stripParentheses((PsiExpression)arguments[i + usedArguments].copy())); + } + usedArguments += count; + if (!inStringLiteral) { + if (addPlus) { + newMethodCall.append('+'); + } + newMethodCall.append('"'); + inStringLiteral = true; + } + newMethodCall.append(text.substring(1, text.length() - 1)); + } + else { + if (inStringLiteral) { + newMethodCall.append('"'); + inStringLiteral = false; + } + if (addPlus) { + newMethodCall.append('+'); + } + newMethodCall.append(operand.getText()); + } + } + else { + newArguments.add(ParenthesesUtils.stripParentheses((PsiExpression)operand.copy())); + if (!inStringLiteral) { + if (addPlus) { + newMethodCall.append('+'); + } + newMethodCall.append('"'); + inStringLiteral = true; + } + newMethodCall.append("{}"); + } + addPlus = true; + } + while (usedArguments < arguments.length) { + newArguments.add(arguments[usedArguments++]); + } + if (inStringLiteral) { + newMethodCall.append('"'); + } + if (!varArgs && newArguments.size() > 2) { + newMethodCall.append(", new Object[]{"); + boolean comma = false; + for (PsiExpression newArgument : newArguments) { + if (comma) { + newMethodCall.append(','); + } + else { + comma =true; + } + if (newArgument != null) { + newMethodCall.append(newArgument.getText()); + } + } + newMethodCall.append('}'); + } + else { + for (PsiExpression newArgument : newArguments) { + newMethodCall.append(','); + if (newArgument != null) { + newMethodCall.append(newArgument.getText()); + } + } + } + newMethodCall.append(')'); + replaceExpression(methodCallExpression, newMethodCall.toString()); + } + + public static boolean isAvailable(PsiExpression expression) { + if (!(expression instanceof PsiPolyadicExpression)) { + return false; + } + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; + final PsiExpression[] operands = polyadicExpression.getOperands(); + for (PsiExpression operand : operands) { + if (!ExpressionUtils.isEvaluatedAtCompileTime(operand)) { + return true; + } + } + return false; + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new StringConcatenationArgumentToLogCallVisitor(); + } + + private static class StringConcatenationArgumentToLogCallVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final String referenceName = methodExpression.getReferenceName(); + if (!logNames.contains(referenceName)) { + return; + } + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null || !"org.slf4j.Logger".equals(containingClass.getQualifiedName())) { + return; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length == 0) { + return; + } + PsiExpression argument = arguments[0]; + if (!ExpressionUtils.hasStringType(argument)) { + if (arguments.length < 2) { + return; + } + argument = arguments[1]; + if (!ExpressionUtils.hasStringType(argument)) { + return; + } + } + if (!containsNonConstantConcatenation(argument)) { + return; + } + registerMethodCallError(expression, argument); + } + + private static boolean containsNonConstantConcatenation(@Nullable PsiExpression expression) { + if (expression instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression; + return containsNonConstantConcatenation(parenthesizedExpression.getExpression()); + } + else if (expression instanceof PsiPolyadicExpression) { + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; + if (!ExpressionUtils.hasStringType(polyadicExpression)) { + return false; + } + if (!JavaTokenType.PLUS.equals(polyadicExpression.getOperationTokenType())) { + return false; + } + final PsiExpression[] operands = polyadicExpression.getOperands(); + for (PsiExpression operand : operands) { + if (!ExpressionUtils.isEvaluatedAtCompileTime(operand)) { + return true; + } + } + } + return false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/maturity/TodoCommentInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/maturity/TodoCommentInspection.java new file mode 100644 index 000000000000..9b79d5453758 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/maturity/TodoCommentInspection.java @@ -0,0 +1,54 @@ +/* + * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.maturity; + +import com.intellij.psi.PsiComment; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class TodoCommentInspection extends BaseInspection { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("todo.comment.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("todo.comment.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ClassWithoutToStringVisitor(); + } + + private static class ClassWithoutToStringVisitor + extends BaseInspectionVisitor { + + @Override + public void visitComment(PsiComment comment) { + super.visitComment(comment); + if (TodoUtil.isTodoComment(comment)) { + registerError(comment); + } + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/maturity/TodoUtil.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/maturity/TodoUtil.java new file mode 100644 index 000000000000..a75f9185bf3e --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/maturity/TodoUtil.java @@ -0,0 +1,42 @@ +/* + * Copyright 2003-2005 Dave Griffith + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.maturity; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiComment; +import com.intellij.psi.PsiFile; +import com.intellij.psi.search.PsiTodoSearchHelper; +import com.intellij.psi.search.TodoItem; + +public class TodoUtil { + private TodoUtil() { + super(); + } + + public static boolean isTodoComment(PsiComment comment) { + final PsiFile file = comment.getContainingFile(); + final PsiTodoSearchHelper searchHelper = PsiTodoSearchHelper.SERVICE.getInstance(comment.getProject()); + final TodoItem[] todoItems = searchHelper.findTodoItems(file); + for (final TodoItem todoItem : todoItems) { + final TextRange commentTextRange = comment.getTextRange(); + final TextRange todoTextRange = todoItem.getTextRange(); + if (commentTextRange.contains(todoTextRange)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/ZeroLengthArrayInitializationInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/ZeroLengthArrayInitializationInspectionBase.java new file mode 100644 index 000000000000..05d57bb072c0 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/ZeroLengthArrayInitializationInspectionBase.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.memory; + +import com.intellij.psi.PsiArrayInitializerExpression; +import com.intellij.psi.PsiExpression; +import com.intellij.psi.PsiNewExpression; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.jetbrains.annotations.NotNull; + +public class ZeroLengthArrayInitializationInspectionBase extends BaseInspection { + @Override + @NotNull + public String getID() { + return "ZeroLengthArrayAllocation"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "array.allocation.zero.length.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "array.allocation.zero.length.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ZeroLengthArrayInitializationVisitor(); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + private static class ZeroLengthArrayInitializationVisitor + extends BaseInspectionVisitor { + + @Override + public void visitNewExpression( + @NotNull PsiNewExpression expression) { + super.visitNewExpression(expression); + if (!ExpressionUtils.isZeroLengthArrayConstruction(expression)) { + return; + } + if (ExpressionUtils.isDeclaredConstant(expression)) { + return; + } + registerError(expression); + } + + @Override + public void visitArrayInitializerExpression( + PsiArrayInitializerExpression expression) { + super.visitArrayInitializerExpression(expression); + final PsiExpression[] initializers = expression.getInitializers(); + if (initializers.length > 0) { + return; + } + if (expression.getParent() instanceof PsiNewExpression) { + return; + } + if (ExpressionUtils.isDeclaredConstant(expression)) { + return; + } + registerError(expression); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/CollectionsFieldAccessReplaceableByMethodCallInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/CollectionsFieldAccessReplaceableByMethodCallInspection.java new file mode 100644 index 000000000000..a8b59188e92d --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/CollectionsFieldAccessReplaceableByMethodCallInspection.java @@ -0,0 +1,223 @@ +/* + * Copyright 2008-2011 Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ExpectedTypeUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CollectionsFieldAccessReplaceableByMethodCallInspection + extends BaseInspection { + + @Override + @Nls + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "collections.field.access.replaceable.by.method.call.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "collections.field.access.replaceable.by.method.call.problem.descriptor", + infos[1]); + } + + @Override + @Nullable + protected InspectionGadgetsFix buildFix(Object... infos) { + final PsiReferenceExpression expression = + (PsiReferenceExpression)infos[0]; + return new CollectionsFieldAccessReplaceableByMethodCallFix( + expression.getReferenceName()); + } + + private static class CollectionsFieldAccessReplaceableByMethodCallFix + extends InspectionGadgetsFix { + + private final String replacementText; + + private CollectionsFieldAccessReplaceableByMethodCallFix( + String referenceName) { + replacementText = getCollectionsMethodCallText(referenceName); + } + + @NotNull + @Override + public String getFamilyName() { + return "Replace Collections.EMPTY_* with call"; + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message( + "collections.field.access.replaceable.by.method.call.quickfix", + replacementText); + } + + @NonNls + private static String getCollectionsMethodCallText( + PsiReferenceExpression referenceExpression) { + final String referenceName = referenceExpression.getReferenceName(); + final PsiElement parent = referenceExpression.getParent(); + if (!(parent instanceof PsiExpressionList)) { + return getUntypedCollectionsMethodCallText(referenceName); + } + final PsiType type = ExpectedTypeUtils.findExpectedType( + referenceExpression, false); + if (!(type instanceof PsiClassType)) { + return getUntypedCollectionsMethodCallText(referenceName); + } + final PsiClassType classType = (PsiClassType)type; + final PsiType[] parameterTypes = classType.getParameters(); + boolean useTypeParameter = false; + final String[] canonicalTexts = new String[parameterTypes.length]; + for (int i = 0, parameterTypesLength = parameterTypes.length; + i < parameterTypesLength; i++) { + final PsiType parameterType = parameterTypes[i]; + if (parameterType instanceof PsiWildcardType) { + final PsiWildcardType wildcardType = + (PsiWildcardType)parameterType; + final PsiType bound = wildcardType.getBound(); + if (bound != null) { + if (!bound.equalsToText( + CommonClassNames.JAVA_LANG_OBJECT)) { + useTypeParameter = true; + } + canonicalTexts[i] = bound.getCanonicalText(); + } + else { + canonicalTexts[i] = CommonClassNames.JAVA_LANG_OBJECT; + } + } + else { + if (!parameterType.equalsToText( + CommonClassNames.JAVA_LANG_OBJECT)) { + useTypeParameter = true; + } + canonicalTexts[i] = parameterType.getCanonicalText(); + } + } + if (useTypeParameter) { + return "Collections.<" + StringUtil.join(canonicalTexts, ",") + + '>' + getCollectionsMethodCallText(referenceName); + } + else { + return getUntypedCollectionsMethodCallText(referenceName); + } + } + + @NonNls + private static String getUntypedCollectionsMethodCallText( + String referenceName) { + return "Collections." + getCollectionsMethodCallText(referenceName); + } + + @NonNls + private static String getCollectionsMethodCallText( + @NonNls String referenceName) { + if ("EMPTY_LIST".equals(referenceName)) { + return "emptyList()"; + } + else if ("EMPTY_MAP".equals(referenceName)) { + return "emptyMap()"; + } + else if ("EMPTY_SET".equals(referenceName)) { + return "emptySet()"; + } + else { + throw new AssertionError("unknown collections field name: " + + referenceName); + } + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) + throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + if (!(element instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)element; + final String newMethodCallText = + getCollectionsMethodCallText(referenceExpression); + replaceExpression(referenceExpression, + "java.util." + newMethodCallText); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new CollectionsFieldAccessReplaceableByMethodCallVisitor(); + } + + private static class CollectionsFieldAccessReplaceableByMethodCallVisitor + extends BaseInspectionVisitor { + + @Override + public void visitReferenceExpression( + PsiReferenceExpression expression) { + if (!PsiUtil.isLanguageLevel5OrHigher(expression)) { + return; + } + super.visitReferenceExpression(expression); + @NonNls final String name = expression.getReferenceName(); + @NonNls final String replacement; + if ("EMPTY_LIST".equals(name)) { + replacement = "emptyList()"; + } + else if ("EMPTY_MAP".equals(name)) { + replacement = "emptyMap()"; + } + else if ("EMPTY_SET".equals(name)) { + replacement = "emptySet()"; + } + else { + return; + } + final PsiElement target = expression.resolve(); + if (!(target instanceof PsiField)) { + return; + } + final PsiField field = (PsiField)target; + final PsiClass containingClass = field.getContainingClass(); + if (containingClass == null) { + return; + } + final String qualifiedName = containingClass.getQualifiedName(); + if (!CommonClassNames.JAVA_UTIL_COLLECTIONS.equals(qualifiedName)) { + return; + } + registerError(expression, expression, replacement); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/EnumerationCanBeIterationInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/EnumerationCanBeIterationInspectionBase.java new file mode 100644 index 000000000000..a12c193d42dc --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/EnumerationCanBeIterationInspectionBase.java @@ -0,0 +1,195 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TypeUtils; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class EnumerationCanBeIterationInspectionBase extends BaseInspection { + protected static final int KEEP_NOTHING = 0; + protected static final int KEEP_INITIALIZATION = 1; + protected static final int KEEP_DECLARATION = 2; + @NonNls + static final String ITERATOR_TEXT = "iterator()"; + @NonNls + static final String KEY_SET_ITERATOR_TEXT = "keySet().iterator()"; + @NonNls + static final String VALUES_ITERATOR_TEXT = "values().iterator()"; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "enumeration.can.be.iteration.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "enumeration.can.be.iteration.problem.descriptor", infos[0]); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new EnumerationCanBeIterationVisitor(); + } + + private static class EnumerationCanBeIterationVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression( + PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = + expression.getMethodExpression(); + @NonNls final String methodName = + methodExpression.getReferenceName(); + final boolean isElements; + if ("elements".equals(methodName)) { + isElements = true; + } + else if ("keys".equals(methodName)) { + isElements = false; + } + else { + return; + } + if (!TypeUtils.expressionHasTypeOrSubtype(expression, + "java.util.Enumeration")) { + return; + } + final PsiElement parent = expression.getParent(); + final PsiVariable variable; + if (parent instanceof PsiLocalVariable) { + variable = (PsiVariable)parent; + } + else if (parent instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression assignmentExpression = + (PsiAssignmentExpression)parent; + final PsiExpression lhs = assignmentExpression.getLExpression(); + if (!(lhs instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)lhs; + final PsiElement element = referenceExpression.resolve(); + if (!(element instanceof PsiVariable)) { + return; + } + variable = (PsiVariable)element; + } + else { + return; + } + final PsiMethod containingMethod = PsiTreeUtil.getParentOfType( + expression, PsiMethod.class); + if (containingMethod == null) { + return; + } + if (!isEnumerationMethodCalled(variable, containingMethod)) { + return; + } + if (isElements) { + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (InheritanceUtil.isInheritor(containingClass, + "java.util.Vector")) { + registerMethodCallError(expression, ITERATOR_TEXT); + } + else if (InheritanceUtil.isInheritor(containingClass, + "java.util.Hashtable")) { + registerMethodCallError(expression, VALUES_ITERATOR_TEXT); + } + } + else { + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (InheritanceUtil.isInheritor(containingClass, + "java.util.Hashtable")) { + registerMethodCallError(expression, KEY_SET_ITERATOR_TEXT); + } + } + } + + private static boolean isEnumerationMethodCalled( + @NotNull PsiVariable variable, @NotNull PsiElement context) { + final EnumerationCanBeIterationVisitor.EnumerationMethodCalledVisitor visitor = + new EnumerationCanBeIterationVisitor.EnumerationMethodCalledVisitor(variable); + context.accept(visitor); + return visitor.isEnumerationMethodCalled(); + } + + private static class EnumerationMethodCalledVisitor + extends JavaRecursiveElementVisitor { + + private final PsiVariable variable; + private boolean enumerationMethodCalled = false; + + EnumerationMethodCalledVisitor(@NotNull PsiVariable variable) { + this.variable = variable; + } + + @Override + public void visitMethodCallExpression( + PsiMethodCallExpression expression) { + if (enumerationMethodCalled) { + return; + } + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = + expression.getMethodExpression(); + @NonNls final String methodName = + methodExpression.getReferenceName(); + if (!"hasMoreElements".equals(methodName) && + !"nextElement".equals(methodName)) { + return; + } + final PsiExpression qualifierExpression = + methodExpression.getQualifierExpression(); + if (!(qualifierExpression instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)qualifierExpression; + final PsiElement element = referenceExpression.resolve(); + if (!(element instanceof PsiVariable)) { + return; + } + final PsiVariable variable = (PsiVariable)element; + enumerationMethodCalled = this.variable.equals(variable); + } + + public boolean isEnumerationMethodCalled() { + return enumerationMethodCalled; + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/ForCanBeForeachInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/ForCanBeForeachInspectionBase.java new file mode 100644 index 000000000000..f2597139bed2 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/ForCanBeForeachInspectionBase.java @@ -0,0 +1,920 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.*; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ForCanBeForeachInspectionBase extends BaseInspection { + @SuppressWarnings("PublicField") + public boolean REPORT_INDEXED_LOOP = true; + @SuppressWarnings("PublicField") + public boolean ignoreUntypedCollections = false; + + protected static boolean isIndexedListLoopStatement(PsiForStatement forStatement, boolean ignoreUntypedCollections) { + final PsiStatement initialization = forStatement.getInitialization(); + if (!(initialization instanceof PsiDeclarationStatement)) { + return false; + } + final PsiDeclarationStatement declaration = (PsiDeclarationStatement)initialization; + final PsiElement[] declaredElements = declaration.getDeclaredElements(); + final PsiElement secondDeclaredElement; + if (declaredElements.length == 1) { + secondDeclaredElement = null; + } + else if (declaredElements.length == 2) { + secondDeclaredElement = declaredElements[1]; + } + else { + return false; + } + final PsiElement declaredElement = declaredElements[0]; + if (!(declaredElement instanceof PsiVariable)) { + return false; + } + final PsiVariable indexVariable = (PsiVariable)declaredElement; + final PsiExpression initialValue = indexVariable.getInitializer(); + if (initialValue == null) { + return false; + } + final Object constant = ExpressionUtils.computeConstantExpression(initialValue); + if (!(constant instanceof Number)) { + return false; + } + final Number number = (Number)constant; + if (number.intValue() != 0) { + return false; + } + final PsiExpression condition = forStatement.getCondition(); + final Holder collectionHolder = getCollectionFromSizeComparison(condition, indexVariable, secondDeclaredElement); + if (collectionHolder == null) { + return false; + } + final PsiStatement update = forStatement.getUpdate(); + if (!VariableAccessUtils.variableIsIncremented(indexVariable, update)) { + return false; + } + final PsiStatement body = forStatement.getBody(); + if (!isIndexVariableOnlyUsedAsListIndex(collectionHolder, indexVariable, body)) { + return false; + } + if (collectionHolder != Holder.DUMMY) { + final PsiVariable collection = collectionHolder.getVariable(); + final PsiClassType collectionType = (PsiClassType)collection.getType(); + final PsiType[] parameters = collectionType.getParameters(); + if (ignoreUntypedCollections && parameters.length == 0) { + return false; + } + return !VariableAccessUtils.variableIsAssigned(collection, body); + } + return true; + } + + static boolean isArrayLoopStatement(PsiForStatement forStatement) { + final PsiStatement initialization = forStatement.getInitialization(); + if (!(initialization instanceof PsiDeclarationStatement)) { + return false; + } + final PsiDeclarationStatement declaration = (PsiDeclarationStatement)initialization; + final PsiElement[] declaredElements = declaration.getDeclaredElements(); + final PsiElement secondDeclaredElement; + if (declaredElements.length == 1) { + secondDeclaredElement = null; + } + else if (declaredElements.length == 2) { + secondDeclaredElement = declaredElements[1]; + } + else { + return false; + } + final PsiElement declaredElement = declaredElements[0]; + if (!(declaredElement instanceof PsiVariable)) { + return false; + } + final PsiVariable indexVariable = (PsiVariable)declaredElement; + final PsiExpression initialValue = indexVariable.getInitializer(); + if (initialValue == null) { + return false; + } + final Object constant = ExpressionUtils.computeConstantExpression(initialValue); + if (!(constant instanceof Integer)) { + return false; + } + final Integer integer = (Integer)constant; + if (integer.intValue() != 0) { + return false; + } + final PsiStatement update = forStatement.getUpdate(); + if (!VariableAccessUtils.variableIsIncremented(indexVariable, update)) { + return false; + } + final PsiExpression condition = forStatement.getCondition(); + final PsiReferenceExpression arrayReference = getVariableReferenceFromCondition(condition, indexVariable, secondDeclaredElement); + if (arrayReference == null) { + return false; + } + if (!(arrayReference.getType() instanceof PsiArrayType)) { + return false; + } + final PsiElement element = arrayReference.resolve(); + if (!(element instanceof PsiVariable)) { + return false; + } + final PsiVariable arrayVariable = (PsiVariable)element; + final PsiStatement body = forStatement.getBody(); + return body == null || + isIndexVariableOnlyUsedAsIndex(arrayVariable, indexVariable, body) && + !VariableAccessUtils.variableIsAssigned(arrayVariable, body) && + !VariableAccessUtils.arrayContentsAreAssigned(arrayVariable, body); + } + + private static boolean isIndexVariableOnlyUsedAsIndex( + @NotNull PsiVariable arrayVariable, + @NotNull PsiVariable indexVariable, + @Nullable PsiStatement body) { + if (body == null) { + return true; + } + final VariableOnlyUsedAsIndexVisitor visitor = + new VariableOnlyUsedAsIndexVisitor(arrayVariable, indexVariable); + body.accept(visitor); + return visitor.isIndexVariableUsedOnlyAsIndex(); + } + + private static boolean isIndexVariableOnlyUsedAsListIndex( + Holder collectionHolder, PsiVariable indexVariable, + PsiStatement body) { + if (body == null) { + return true; + } + final VariableOnlyUsedAsListIndexVisitor visitor = + new VariableOnlyUsedAsListIndexVisitor(collectionHolder, + indexVariable); + body.accept(visitor); + return visitor.isIndexVariableUsedOnlyAsIndex(); + } + + static boolean isCollectionLoopStatement( + PsiForStatement forStatement, boolean ignoreUntypedCollections) { + final PsiStatement initialization = forStatement.getInitialization(); + if (!(initialization instanceof PsiDeclarationStatement)) { + return false; + } + final PsiDeclarationStatement declaration = + (PsiDeclarationStatement)initialization; + final PsiElement[] declaredElements = declaration.getDeclaredElements(); + if (declaredElements.length != 1) { + return false; + } + final PsiElement declaredElement = declaredElements[0]; + if (!(declaredElement instanceof PsiVariable)) { + return false; + } + final PsiVariable variable = (PsiVariable)declaredElement; + if (!TypeUtils.variableHasTypeOrSubtype(variable, + CommonClassNames.JAVA_UTIL_ITERATOR, + "java.util.ListIterator")) { + return false; + } + final PsiExpression initialValue = variable.getInitializer(); + if (initialValue == null) { + return false; + } + if (!(initialValue instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression initialCall = + (PsiMethodCallExpression)initialValue; + final PsiReferenceExpression initialMethodExpression = + initialCall.getMethodExpression(); + @NonNls final String initialCallName = + initialMethodExpression.getReferenceName(); + if (!HardcodedMethodConstants.ITERATOR.equals(initialCallName) && + !"listIterator".equals(initialCallName)) { + return false; + } + final PsiExpressionList argumentList = initialCall.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 0) { + return false; + } + final PsiExpression qualifier = + initialMethodExpression.getQualifierExpression(); + final PsiClass qualifierClass; + if (qualifier == null) { + qualifierClass = + ClassUtils.getContainingClass(initialMethodExpression); + if (ignoreUntypedCollections) { + final PsiClassType type = (PsiClassType)variable.getType(); + final PsiType[] parameters = type.getParameters(); + if (parameters.length == 0) { + return false; + } + } + } + else { + final PsiType qualifierType = qualifier.getType(); + if (!(qualifierType instanceof PsiClassType)) { + return false; + } + final PsiClassType classType = (PsiClassType)qualifierType; + qualifierClass = classType.resolve(); + if (ignoreUntypedCollections) { + final PsiClassType type = (PsiClassType)variable.getType(); + final PsiType[] parameters = type.getParameters(); + final PsiType[] parameters1 = classType.getParameters(); + if (parameters.length == 0 && parameters1.length == 0) { + return false; + } + } + } + if (qualifierClass == null) { + return false; + } + if (!InheritanceUtil.isInheritor(qualifierClass, + CommonClassNames.JAVA_LANG_ITERABLE) && + !InheritanceUtil.isInheritor(qualifierClass, + CommonClassNames.JAVA_UTIL_COLLECTION)) { + return false; + } + final PsiExpression condition = forStatement.getCondition(); + if (!isHasNext(condition, variable)) { + return false; + } + final PsiStatement update = forStatement.getUpdate(); + if (update != null && !(update instanceof PsiEmptyStatement)) { + return false; + } + final PsiStatement body = forStatement.getBody(); + if (body == null) { + return false; + } + if (calculateCallsToIteratorNext(variable, body) != 1) { + return false; + } + if (isIteratorMethodCalled(variable, body)) { + return false; + } + return !VariableAccessUtils.variableIsReturned(variable, body) && + !VariableAccessUtils.variableIsAssigned(variable, body) && + !VariableAccessUtils.variableIsPassedAsMethodArgument(variable, body); + } + + private static int calculateCallsToIteratorNext(PsiVariable iterator, + PsiStatement body) { + if (body == null) { + return 0; + } + final NumCallsToIteratorNextVisitor visitor = + new NumCallsToIteratorNextVisitor(iterator); + body.accept(visitor); + return visitor.getNumCallsToIteratorNext(); + } + + private static boolean isIteratorMethodCalled(PsiVariable iterator, + PsiStatement body) { + final IteratorMethodCallVisitor visitor = + new IteratorMethodCallVisitor(iterator); + body.accept(visitor); + return visitor.isMethodCalled(); + } + + private static boolean isHasNext(PsiExpression condition, + PsiVariable iterator) { + if (!(condition instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression call = + (PsiMethodCallExpression)condition; + final PsiExpressionList argumentList = call.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 0) { + return false; + } + final PsiReferenceExpression methodExpression = + call.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.HAS_NEXT.equals(methodName)) { + return false; + } + final PsiExpression qualifier = + methodExpression.getQualifierExpression(); + if (qualifier == null) { + return true; + } + if (!(qualifier instanceof PsiReferenceExpression)) { + return false; + } + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + return iterator.equals(target); + } + + @Nullable + private static PsiReferenceExpression getVariableReferenceFromCondition(PsiExpression condition, + PsiVariable variable, + PsiElement secondDeclaredElement) { + condition = ParenthesesUtils.stripParentheses(condition); + if (!(condition instanceof PsiBinaryExpression)) { + return null; + } + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)condition; + final IElementType tokenType = binaryExpression.getOperationTokenType(); + final PsiExpression lhs = ParenthesesUtils.stripParentheses(binaryExpression.getLOperand()); + final PsiExpression rhs = ParenthesesUtils.stripParentheses(binaryExpression.getROperand()); + if (rhs == null) { + return null; + } + PsiReferenceExpression referenceExpression; + if (tokenType.equals(JavaTokenType.LT)) { + if (!VariableAccessUtils.evaluatesToVariable(lhs, variable) || !(rhs instanceof PsiReferenceExpression)) { + return null; + } + referenceExpression = (PsiReferenceExpression)rhs; + } + else if (tokenType.equals(JavaTokenType.GT)) { + if (!VariableAccessUtils.evaluatesToVariable(rhs, variable) || !(lhs instanceof PsiReferenceExpression)) { + return null; + } + referenceExpression = (PsiReferenceExpression)lhs; + } + else { + return null; + } + if (!expressionIsArrayLengthLookup(referenceExpression)) { + final PsiElement target = referenceExpression.resolve(); + if (secondDeclaredElement != null && !secondDeclaredElement.equals(target)) { + return null; + } + if (target instanceof PsiVariable) { + final PsiVariable maxVariable = (PsiVariable)target; + final PsiCodeBlock context = PsiTreeUtil.getParentOfType(maxVariable, PsiCodeBlock.class); + if (context == null) { + return null; + } + if (VariableAccessUtils.variableIsAssigned(maxVariable, context)) { + return null; + } + final PsiExpression expression = ParenthesesUtils.stripParentheses(maxVariable.getInitializer()); + if (!(expression instanceof PsiReferenceExpression)) { + return null; + } + referenceExpression = (PsiReferenceExpression)expression; + if (!expressionIsArrayLengthLookup(referenceExpression)) { + return null; + } + } + } + else { + if (secondDeclaredElement != null) { + return null; + } + } + final PsiExpression qualifierExpression = referenceExpression.getQualifierExpression(); + if (qualifierExpression instanceof PsiReferenceExpression) { + return (PsiReferenceExpression)qualifierExpression; + } + else if (qualifierExpression instanceof PsiThisExpression || + qualifierExpression instanceof PsiSuperExpression || + qualifierExpression == null) { + return referenceExpression; + } + else { + return null; + } + } + + @Nullable + private static Holder getCollectionFromSizeComparison(PsiExpression condition, PsiVariable variable, PsiElement secondDeclaredElement) { + condition = ParenthesesUtils.stripParentheses(condition); + if (!(condition instanceof PsiBinaryExpression)) { + return null; + } + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)condition; + final IElementType tokenType = binaryExpression.getOperationTokenType(); + final PsiExpression rhs = binaryExpression.getROperand(); + final PsiExpression lhs = binaryExpression.getLOperand(); + if (tokenType.equals(JavaTokenType.LT)) { + if (!VariableAccessUtils.evaluatesToVariable(lhs, variable)) { + return null; + } + return getCollectionFromListMethodCall(rhs, HardcodedMethodConstants.SIZE, secondDeclaredElement); + } + else if (tokenType.equals(JavaTokenType.GT)) { + if (!VariableAccessUtils.evaluatesToVariable(rhs, variable)) { + return null; + } + return getCollectionFromListMethodCall(lhs, HardcodedMethodConstants.SIZE, secondDeclaredElement); + } + return null; + } + + static boolean expressionIsListGetLookup(PsiExpression expression) { + expression = ParenthesesUtils.stripParentheses(expression); + if (!(expression instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression reference = + (PsiMethodCallExpression)expression; + final PsiReferenceExpression methodExpression = + reference.getMethodExpression(); + final PsiElement resolved = methodExpression.resolve(); + if (!(resolved instanceof PsiMethod)) { + return false; + } + final PsiMethod method = (PsiMethod)resolved; + if (!HardcodedMethodConstants.GET.equals(method.getName())) { + return false; + } + final PsiClass aClass = method.getContainingClass(); + return InheritanceUtil.isInheritor(aClass, + CommonClassNames.JAVA_UTIL_LIST); + } + + @Nullable + private static Holder getCollectionFromListMethodCall(PsiExpression expression, String methodName, PsiElement secondDeclaredElement) { + expression = ParenthesesUtils.stripParentheses(expression); + if (expression instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression; + final PsiElement target = referenceExpression.resolve(); + if (secondDeclaredElement != null && !secondDeclaredElement.equals(target)) { + return null; + } + if (!(target instanceof PsiVariable)) { + return null; + } + final PsiVariable variable = (PsiVariable)target; + final PsiCodeBlock context = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); + if (context == null) { + return null; + } + if (VariableAccessUtils.variableIsAssigned(variable, context)) { + return null; + } + expression = ParenthesesUtils.stripParentheses(variable.getInitializer()); + } + else if (secondDeclaredElement != null) { + return null; + } + if (!(expression instanceof PsiMethodCallExpression)) { + return null; + } + final PsiMethodCallExpression methodCallExpression = + (PsiMethodCallExpression)expression; + final PsiReferenceExpression methodExpression = + methodCallExpression.getMethodExpression(); + final String referenceName = methodExpression.getReferenceName(); + if (!methodName.equals(referenceName)) { + return null; + } + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method == null) { + return null; + } + final PsiClass containingClass = method.getContainingClass(); + if (!InheritanceUtil.isInheritor(containingClass, + CommonClassNames.JAVA_UTIL_LIST)) { + return null; + } + final PsiExpression qualifierExpression = + ParenthesesUtils.stripParentheses( + methodExpression.getQualifierExpression()); + if (qualifierExpression == null || + qualifierExpression instanceof PsiThisExpression || + qualifierExpression instanceof PsiSuperExpression) { + return Holder.DUMMY; + } + if (!(qualifierExpression instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)qualifierExpression; + final PsiElement target = referenceExpression.resolve(); + if (!(target instanceof PsiVariable)) { + return null; + } + final PsiVariable variable = (PsiVariable)target; + return new Holder(variable); + } + + private static boolean expressionIsArrayLengthLookup(PsiExpression expression) { + expression = ParenthesesUtils.stripParentheses(expression); + if (!(expression instanceof PsiReferenceExpression)) { + return false; + } + final PsiReferenceExpression reference = + (PsiReferenceExpression)expression; + final String referenceName = reference.getReferenceName(); + if (!HardcodedMethodConstants.LENGTH.equals(referenceName)) { + return false; + } + final PsiExpression qualifier = reference.getQualifierExpression(); + if (!(qualifier instanceof PsiReferenceExpression)) { + return false; + } + final PsiType type = qualifier.getType(); + return type != null && type.getArrayDimensions() > 0; + } + + @Override + @NotNull + public String getID() { + return "ForLoopReplaceableByForEach"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "for.can.be.foreach.display.name"); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "for.can.be.foreach.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ForCanBeForeachVisitor(); + } + + private static class NumCallsToIteratorNextVisitor + extends JavaRecursiveElementVisitor { + + private int numCallsToIteratorNext = 0; + private final PsiVariable iterator; + + NumCallsToIteratorNextVisitor(PsiVariable iterator) { + this.iterator = iterator; + } + + @Override + public void visitMethodCallExpression( + @NotNull PsiMethodCallExpression callExpression) { + super.visitMethodCallExpression(callExpression); + final PsiReferenceExpression methodExpression = + callExpression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.NEXT.equals(methodName)) { + return; + } + final PsiExpression qualifier = + methodExpression.getQualifierExpression(); + if (qualifier == null) { + return; + } + if (!(qualifier instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + if (!iterator.equals(target)) { + return; + } + numCallsToIteratorNext++; + } + + public int getNumCallsToIteratorNext() { + return numCallsToIteratorNext; + } + } + + private static class IteratorMethodCallVisitor + extends JavaRecursiveElementVisitor { + + private boolean methodCalled = false; + private final PsiVariable iterator; + + IteratorMethodCallVisitor(PsiVariable iterator) { + this.iterator = iterator; + } + + @Override + public void visitElement(@NotNull PsiElement element) { + if (!methodCalled) { + super.visitElement(element); + } + } + + @Override + public void visitMethodCallExpression( + @NotNull PsiMethodCallExpression expression) { + if (methodCalled) { + return; + } + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = + expression.getMethodExpression(); + final String name = methodExpression.getReferenceName(); + if (HardcodedMethodConstants.NEXT.equals(name)) { + return; + } + final PsiExpression qualifier = + methodExpression.getQualifierExpression(); + if (!(qualifier instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + if (iterator.equals(target)) { + methodCalled = true; + } + } + + public boolean isMethodCalled() { + return methodCalled; + } + } + + private static class VariableOnlyUsedAsIndexVisitor + extends JavaRecursiveElementVisitor { + + private boolean indexVariableUsedOnlyAsIndex = true; + private final PsiVariable arrayVariable; + private final PsiVariable indexVariable; + + VariableOnlyUsedAsIndexVisitor(PsiVariable arrayVariable, + PsiVariable indexVariable) { + this.arrayVariable = arrayVariable; + this.indexVariable = indexVariable; + } + + @Override + public void visitElement(@NotNull PsiElement element) { + if (indexVariableUsedOnlyAsIndex) { + super.visitElement(element); + } + } + + @Override + public void visitReferenceExpression( + @NotNull PsiReferenceExpression reference) { + if (!indexVariableUsedOnlyAsIndex) { + return; + } + super.visitReferenceExpression(reference); + final PsiElement element = reference.resolve(); + if (!indexVariable.equals(element)) { + return; + } + final PsiElement parent = reference.getParent(); + if (!(parent instanceof PsiArrayAccessExpression)) { + indexVariableUsedOnlyAsIndex = false; + return; + } + final PsiArrayAccessExpression arrayAccessExpression = + (PsiArrayAccessExpression)parent; + final PsiExpression arrayExpression = + arrayAccessExpression.getArrayExpression(); + if (!(arrayExpression instanceof PsiReferenceExpression)) { + indexVariableUsedOnlyAsIndex = false; + return; + } + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)arrayExpression; + final PsiExpression qualifier = + referenceExpression.getQualifierExpression(); + if (qualifier != null && !(qualifier instanceof PsiThisExpression) + && !(qualifier instanceof PsiSuperExpression)) { + indexVariableUsedOnlyAsIndex = false; + return; + } + final PsiElement target = referenceExpression.resolve(); + if (!arrayVariable.equals(target)) { + indexVariableUsedOnlyAsIndex = false; + return; + } + final PsiElement arrayExpressionContext = + arrayAccessExpression.getParent(); + if (arrayExpressionContext instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression assignment = + (PsiAssignmentExpression)arrayExpressionContext; + final PsiExpression lhs = assignment.getLExpression(); + if (lhs.equals(arrayAccessExpression)) { + indexVariableUsedOnlyAsIndex = false; + } + } + } + + public boolean isIndexVariableUsedOnlyAsIndex() { + return indexVariableUsedOnlyAsIndex; + } + } + + private static class VariableOnlyUsedAsListIndexVisitor + extends JavaRecursiveElementVisitor { + + private boolean indexVariableUsedOnlyAsIndex = true; + private final PsiVariable indexVariable; + private final Holder collection; + + VariableOnlyUsedAsListIndexVisitor( + @NotNull Holder collection, + @NotNull PsiVariable indexVariable) { + this.collection = collection; + this.indexVariable = indexVariable; + } + + @Override + public void visitElement(@NotNull PsiElement element) { + if (indexVariableUsedOnlyAsIndex) { + super.visitElement(element); + } + } + + @Override + public void visitReferenceExpression( + @NotNull PsiReferenceExpression reference) { + if (!indexVariableUsedOnlyAsIndex) { + return; + } + super.visitReferenceExpression(reference); + final PsiElement element = reference.resolve(); + if (indexVariable.equals(element)) { + if (!isListIndexExpression(reference)) { + indexVariableUsedOnlyAsIndex = false; + } + } + else if (collection == Holder.DUMMY) { + if (isListNonGetMethodCall(reference)) { + indexVariableUsedOnlyAsIndex = false; + } + } + else if (collection.getVariable().equals(element) && + !isListReferenceInIndexExpression(reference)) { + indexVariableUsedOnlyAsIndex = false; + } + } + + public boolean isIndexVariableUsedOnlyAsIndex() { + return indexVariableUsedOnlyAsIndex; + } + + private boolean isListNonGetMethodCall( + PsiReferenceExpression reference) { + final PsiElement parent = reference.getParent(); + if (!(parent instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression methodCallExpression = + (PsiMethodCallExpression)parent; + final PsiMethod method = + methodCallExpression.resolveMethod(); + if (method == null) { + return false; + } + final PsiClass parentClass = PsiTreeUtil.getParentOfType( + methodCallExpression, PsiClass.class); + final PsiClass containingClass = method.getContainingClass(); + if (!InheritanceUtil.isInheritorOrSelf(parentClass, + containingClass, true)) { + return false; + } + return !isListGetExpression(methodCallExpression); + } + + private boolean isListIndexExpression(PsiReferenceExpression reference) { + final PsiElement referenceParent = reference.getParent(); + if (!(referenceParent instanceof PsiExpressionList)) { + return false; + } + final PsiExpressionList expressionList = + (PsiExpressionList)referenceParent; + final PsiElement parent = expressionList.getParent(); + if (!(parent instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression methodCallExpression = + (PsiMethodCallExpression)parent; + return isListGetExpression(methodCallExpression); + } + + private boolean isListReferenceInIndexExpression( + PsiReferenceExpression reference) { + final PsiElement parent = reference.getParent(); + if (!(parent instanceof PsiReferenceExpression)) { + return false; + } + final PsiElement grandParent = parent.getParent(); + if (!(grandParent instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression methodCallExpression = + (PsiMethodCallExpression)grandParent; + final PsiElement greatGrandParent = + methodCallExpression.getParent(); + if (greatGrandParent instanceof PsiExpressionStatement) { + return false; + } + return isListGetExpression(methodCallExpression); + } + + private boolean isListGetExpression( + PsiMethodCallExpression methodCallExpression) { + if (methodCallExpression == null) { + return false; + } + final PsiReferenceExpression methodExpression = + methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = + methodExpression.getQualifierExpression(); + if (!(qualifierExpression instanceof PsiReferenceExpression)) { + if (collection == Holder.DUMMY && + (qualifierExpression == null || + qualifierExpression instanceof PsiThisExpression || + qualifierExpression instanceof PsiSuperExpression)) { + return expressionIsListGetLookup(methodCallExpression); + } + return false; + } + final PsiReferenceExpression reference = + (PsiReferenceExpression)qualifierExpression; + final PsiExpression qualifier = reference.getQualifierExpression(); + if (qualifier != null && !(qualifier instanceof PsiThisExpression) + && !(qualifier instanceof PsiSuperExpression)) { + return false; + } + final PsiElement target = reference.resolve(); + if (collection == Holder.DUMMY || + !collection.getVariable().equals(target)) { + return false; + } + return expressionIsListGetLookup(methodCallExpression); + } + } + + private static class Holder { + + public static final Holder DUMMY = new Holder(); + + private final PsiVariable variable; + + public Holder(@NotNull PsiVariable variable) { + this.variable = variable; + } + + private Holder() { + variable = null; + } + + public PsiVariable getVariable() { + return variable; + } + } + + private class ForCanBeForeachVisitor + extends BaseInspectionVisitor { + + @Override + public void visitForStatement( + @NotNull PsiForStatement forStatement) { + super.visitForStatement(forStatement); + if (!PsiUtil.isLanguageLevel5OrHigher(forStatement)) { + return; + } + if (isArrayLoopStatement(forStatement) + || isCollectionLoopStatement(forStatement, + ignoreUntypedCollections) + || (REPORT_INDEXED_LOOP && + isIndexedListLoopStatement(forStatement, + ignoreUntypedCollections))) { + registerStatementError(forStatement); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/IfCanBeSwitchInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/IfCanBeSwitchInspection.java new file mode 100644 index 000000000000..08ea82310e0e --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/IfCanBeSwitchInspection.java @@ -0,0 +1,547 @@ +/* + * Copyright 2011-2013 Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.codeInsight.NullableNotNullManager; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.ui.DocumentAdapter; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ControlFlowUtils; +import com.siyeh.ig.psiutils.EquivalenceChecker; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import com.siyeh.ig.psiutils.SwitchUtils; +import com.siyeh.ig.psiutils.SwitchUtils.IfStatementBranch; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.text.Document; +import java.awt.*; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +public class IfCanBeSwitchInspection extends BaseInspection { + + @SuppressWarnings({"PublicField"}) + public int minimumBranches = 3; + + @SuppressWarnings({"PublicField"}) + public boolean suggestIntSwitches = false; + + @SuppressWarnings({"PublicField"}) + public boolean suggestEnumSwitches = false; + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("if.can.be.switch.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("if.can.be.switch.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new IfCanBeSwitchFix(minimumBranches); + } + + @Override + public JComponent createOptionsPanel() { + final JPanel panel = new JPanel(new GridBagLayout()); + final JLabel label = new JLabel(InspectionGadgetsBundle.message("if.can.be.switch.minimum.branch.option")); + final NumberFormat formatter = NumberFormat.getIntegerInstance(); + formatter.setParseIntegerOnly(true); + final JFormattedTextField valueField = new JFormattedTextField(formatter); + valueField.setValue(Integer.valueOf(minimumBranches)); + valueField.setColumns(2); + final Document document = valueField.getDocument(); + document.addDocumentListener(new DocumentAdapter() { + @Override + public void textChanged(DocumentEvent e) { + try { + valueField.commitEdit(); + minimumBranches = + ((Number)valueField.getValue()).intValue(); + } + catch (ParseException e1) { + // No luck this time + } + } + }); + final GridBagConstraints constraints = new GridBagConstraints(); + constraints.gridx = 0; + constraints.gridy = 0; + constraints.insets.bottom = 4; + constraints.weightx = 0.0; + constraints.anchor = GridBagConstraints.BASELINE_LEADING; + constraints.fill = GridBagConstraints.NONE; + constraints.insets.right = 10; + panel.add(label, constraints); + constraints.gridx = 1; + constraints.gridy = 0; + constraints.weightx = 1.0; + constraints.insets.right = 0; + panel.add(valueField, constraints); + constraints.gridx = 0; + constraints.gridy = 1; + constraints.gridwidth = 2; + final JCheckBox checkBox1 = new JCheckBox(InspectionGadgetsBundle.message("if.can.be.switch.int.option"), suggestIntSwitches); + final ButtonModel model1 = checkBox1.getModel(); + model1.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + suggestIntSwitches = model1.isSelected(); + } + }); + panel.add(checkBox1, constraints); + constraints.gridy = 2; + constraints.weighty = 1.0; + final JCheckBox checkBox2 = new JCheckBox(InspectionGadgetsBundle.message("if.can.be.switch.enum.option"), suggestEnumSwitches); + final ButtonModel model2 = checkBox2.getModel(); + model2.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + suggestEnumSwitches = model2.isSelected(); + } + }); + panel.add(checkBox2, constraints); + return panel; + } + + private static class IfCanBeSwitchFix extends InspectionGadgetsFix { + private final int myMinimumBranches; + + public IfCanBeSwitchFix(int minimumBranches) { + myMinimumBranches = minimumBranches; + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("if.can.be.switch.quickfix"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement().getParent(); + if (!(element instanceof PsiIfStatement)) { + return; + } + PsiIfStatement ifStatement = (PsiIfStatement)element; + boolean breaksNeedRelabeled = false; + PsiStatement breakTarget = null; + String labelString = ""; + if (ControlFlowUtils.statementContainsNakedBreak(ifStatement)) { + breakTarget = PsiTreeUtil.getParentOfType(ifStatement, PsiLoopStatement.class, PsiSwitchStatement.class); + if (breakTarget != null) { + final PsiElement parent = breakTarget.getParent(); + if (parent instanceof PsiLabeledStatement) { + final PsiLabeledStatement labeledStatement = (PsiLabeledStatement)parent; + labelString = labeledStatement.getLabelIdentifier().getText(); + breakTarget = labeledStatement; + breaksNeedRelabeled = true; + } + else { + labelString = SwitchUtils.findUniqueLabelName(ifStatement, "label"); + breaksNeedRelabeled = true; + } + } + } + final PsiIfStatement statementToReplace = ifStatement; + final PsiExpression switchExpression = SwitchUtils.getSwitchExpression(ifStatement, myMinimumBranches); + if (switchExpression == null) { + return; + } + final List branches = new ArrayList(20); + while (true) { + final PsiExpression condition = ifStatement.getCondition(); + final PsiStatement thenBranch = ifStatement.getThenBranch(); + final IfStatementBranch ifBranch = new IfStatementBranch(thenBranch, false); + extractCaseExpressions(condition, switchExpression, ifBranch); + if (!branches.isEmpty()) { + extractIfComments(ifStatement, ifBranch); + } + extractStatementComments(thenBranch, ifBranch); + branches.add(ifBranch); + final PsiStatement elseBranch = ifStatement.getElseBranch(); + if (elseBranch instanceof PsiIfStatement) { + ifStatement = (PsiIfStatement)elseBranch; + } + else if (elseBranch == null) { + break; + } + else { + final IfStatementBranch elseIfBranch = new IfStatementBranch(elseBranch, true); + final PsiKeyword elseKeyword = ifStatement.getElseElement(); + extractIfComments(elseKeyword, elseIfBranch); + extractStatementComments(elseBranch, elseIfBranch); + branches.add(elseIfBranch); + break; + } + } + + @NonNls final StringBuilder switchStatementText = new StringBuilder(); + switchStatementText.append("switch(").append(switchExpression.getText()).append("){"); + final PsiType type = switchExpression.getType(); + final boolean castToInt = type != null && type.equalsToText(CommonClassNames.JAVA_LANG_INTEGER); + for (IfStatementBranch branch : branches) { + boolean hasConflicts = false; + for (IfStatementBranch testBranch : branches) { + if (branch == testBranch) { + continue; + } + if (branch.topLevelDeclarationsConflictWith(testBranch)) { + hasConflicts = true; + } + } + dumpBranch(branch, castToInt, hasConflicts, breaksNeedRelabeled, labelString, switchStatementText); + } + switchStatementText.append('}'); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(element.getProject()); + final PsiElementFactory factory = psiFacade.getElementFactory(); + if (breaksNeedRelabeled) { + final StringBuilder out = new StringBuilder(); + if (!(breakTarget instanceof PsiLabeledStatement)) { + out.append(labelString).append(':'); + } + termReplace(breakTarget, statementToReplace, switchStatementText, out); + final String newStatementText = out.toString(); + final PsiStatement newStatement = factory.createStatementFromText(newStatementText, element); + breakTarget.replace(newStatement); + } + else { + final PsiStatement newStatement = factory.createStatementFromText(switchStatementText.toString(), element); + statementToReplace.replace(newStatement); + } + } + + @Nullable + public static T getPrevSiblingOfType(@Nullable PsiElement element, @NotNull Class aClass, + @NotNull Class... stopAt) { + if (element == null) { + return null; + } + PsiElement sibling = element.getPrevSibling(); + while (sibling != null && !aClass.isInstance(sibling)) { + for (Class stopClass : stopAt) { + if (stopClass.isInstance(sibling)) { + return null; + } + } + sibling = sibling.getPrevSibling(); + } + return (T)sibling; + } + + private static void extractIfComments(PsiElement element, IfStatementBranch out) { + PsiComment comment = getPrevSiblingOfType(element, PsiComment.class, PsiStatement.class); + while (comment != null) { + final PsiElement sibling = comment.getPrevSibling(); + final String commentText; + if (sibling instanceof PsiWhiteSpace) { + final String whiteSpaceText = sibling.getText(); + if (whiteSpaceText.startsWith("\n")) { + commentText = whiteSpaceText.substring(1) + comment.getText(); + } + else { + commentText = comment.getText(); + } + } + else { + commentText = comment.getText(); + } + out.addComment(commentText); + comment = getPrevSiblingOfType(comment, PsiComment.class, PsiStatement.class); + } + } + + private static void extractStatementComments(PsiElement element, IfStatementBranch out) { + PsiComment comment = getPrevSiblingOfType(element, PsiComment.class, PsiStatement.class, PsiKeyword.class); + while (comment != null) { + final PsiElement sibling = comment.getPrevSibling(); + final String commentText; + if (sibling instanceof PsiWhiteSpace) { + final String whiteSpaceText = sibling.getText(); + if (whiteSpaceText.startsWith("\n")) { + commentText = whiteSpaceText.substring(1) + comment.getText(); + } + else { + commentText = comment.getText(); + } + } + else { + commentText = comment.getText(); + } + out.addStatementComment(commentText); + comment = getPrevSiblingOfType(comment, PsiComment.class, PsiStatement.class, PsiKeyword.class); + } + } + + private static void termReplace(PsiElement target, PsiElement replace, StringBuilder stringToReplaceWith, StringBuilder out) { + if (target.equals(replace)) { + out.append(stringToReplaceWith); + } + else if (target.getChildren().length == 0) { + out.append(target.getText()); + } + else { + final PsiElement[] children = target.getChildren(); + for (final PsiElement child : children) { + termReplace(child, replace, stringToReplaceWith, out); + } + } + } + + private static void extractCaseExpressions(PsiExpression expression, PsiExpression switchExpression, IfStatementBranch branch) { + if (expression instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + final PsiExpression argument = arguments[0]; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + if (EquivalenceChecker.expressionsAreEquivalent(switchExpression, argument)) { + branch.addCaseExpression(qualifierExpression); + } + else { + branch.addCaseExpression(argument); + } + } + else if (expression instanceof PsiPolyadicExpression) { + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; + final PsiExpression[] operands = polyadicExpression.getOperands(); + final IElementType tokenType = polyadicExpression.getOperationTokenType(); + if (JavaTokenType.OROR.equals(tokenType)) { + for (PsiExpression operand : operands) { + extractCaseExpressions(operand, switchExpression, branch); + } + } + else if (operands.length == 2) { + final PsiExpression lhs = operands[0]; + final PsiExpression rhs = operands[1]; + if (EquivalenceChecker.expressionsAreEquivalent(switchExpression, rhs)) { + branch.addCaseExpression(lhs); + } + else { + branch.addCaseExpression(rhs); + } + } + } + else if (expression instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression; + final PsiExpression contents = parenthesizedExpression.getExpression(); + extractCaseExpressions(contents, switchExpression, branch); + } + } + + private static void dumpBranch(IfStatementBranch branch, boolean castToInt, boolean wrap, boolean renameBreaks, String breakLabelName, + @NonNls StringBuilder switchStatementText) { + dumpComments(branch.getComments(), switchStatementText); + if (branch.isElse()) { + switchStatementText.append("default: "); + } + else { + for (PsiExpression caseExpression : branch.getCaseExpressions()) { + switchStatementText.append("case ").append(getCaseLabelText(caseExpression, castToInt)).append(": "); + } + } + dumpComments(branch.getStatementComments(), switchStatementText); + dumpBody(branch.getStatement(), wrap, renameBreaks, breakLabelName, switchStatementText); + } + + @NonNls + private static String getCaseLabelText(PsiExpression expression, boolean castToInt) { + if (expression instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression; + final PsiElement target = referenceExpression.resolve(); + if (target instanceof PsiEnumConstant) { + final PsiEnumConstant enumConstant = (PsiEnumConstant)target; + return enumConstant.getName(); + } + } + if (castToInt) { + final PsiType type = expression.getType(); + if (!PsiType.INT.equals(type)) { + /* + because + Integer a = 1; + switch (a) { + case (byte)7: + } + does not compile with javac (but does with Eclipse) + */ + return "(int)" + expression.getText(); + } + } + return expression.getText(); + } + + private static void dumpComments(List comments, StringBuilder switchStatementText) { + if (comments.isEmpty()) { + return; + } + switchStatementText.append('\n'); + for (String comment : comments) { + switchStatementText.append(comment).append('\n'); + } + } + + private static void dumpBody(PsiStatement bodyStatement, boolean wrap, boolean renameBreaks, String breakLabelName, + @NonNls StringBuilder switchStatementText) { + if (wrap) { + switchStatementText.append('{'); + } + if (bodyStatement instanceof PsiBlockStatement) { + final PsiCodeBlock codeBlock = ((PsiBlockStatement)bodyStatement).getCodeBlock(); + final PsiElement[] children = codeBlock.getChildren(); + //skip the first and last members, to unwrap the block + for (int i = 1; i < children.length - 1; i++) { + final PsiElement child = children[i]; + appendElement(child, renameBreaks, breakLabelName, switchStatementText); + } + } + else { + appendElement(bodyStatement, renameBreaks, breakLabelName, switchStatementText); + } + if (ControlFlowUtils.statementMayCompleteNormally(bodyStatement)) { + switchStatementText.append("break;"); + } + if (wrap) { + switchStatementText.append('}'); + } + } + + private static void appendElement(PsiElement element, boolean renameBreakElements, String breakLabelString, + @NonNls StringBuilder switchStatementText) { + final String text = element.getText(); + if (!renameBreakElements) { + switchStatementText.append(text); + } + else if (element instanceof PsiBreakStatement) { + final PsiBreakStatement breakStatement = (PsiBreakStatement)element; + final PsiIdentifier identifier = breakStatement.getLabelIdentifier(); + if (identifier == null) { + switchStatementText.append("break ").append(breakLabelString).append(';'); + } + else { + switchStatementText.append(text); + } + } + else if (element instanceof PsiBlockStatement || element instanceof PsiCodeBlock || element instanceof PsiIfStatement) { + final PsiElement[] children = element.getChildren(); + for (final PsiElement child : children) { + appendElement(child, renameBreakElements, breakLabelString, switchStatementText); + } + } + else { + switchStatementText.append(text); + } + final PsiElement lastChild = element.getLastChild(); + if (isEndOfLineComment(lastChild)) { + switchStatementText.append('\n'); + } + } + + private static boolean isEndOfLineComment(PsiElement element) { + if (!(element instanceof PsiComment)) { + return false; + } + final PsiComment comment = (PsiComment)element; + final IElementType tokenType = comment.getTokenType(); + return JavaTokenType.END_OF_LINE_COMMENT.equals(tokenType); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new IfCanBeSwitchVisitor(); + } + + private class IfCanBeSwitchVisitor extends BaseInspectionVisitor { + + @Override + public void visitIfStatement(PsiIfStatement statement) { + super.visitIfStatement(statement); + final PsiElement parent = statement.getParent(); + if (parent instanceof PsiIfStatement) { + return; + } + final PsiExpression switchExpression = SwitchUtils.getSwitchExpression(statement, minimumBranches); + if (switchExpression == null) { + return; + } + final PsiExpression unwrappedExpression = ParenthesesUtils.stripParentheses(switchExpression); + if (unwrappedExpression instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)unwrappedExpression; + final PsiElement target = referenceExpression.resolve(); + if (target instanceof PsiModifierListOwner && NullableNotNullManager.isNullable((PsiModifierListOwner)target)) { + return; + } + } + final PsiType type = switchExpression.getType(); + if (!suggestIntSwitches) { + if (type instanceof PsiClassType) { + if (type.equalsToText(CommonClassNames.JAVA_LANG_INTEGER) || + type.equalsToText(CommonClassNames.JAVA_LANG_SHORT) || + type.equalsToText(CommonClassNames.JAVA_LANG_BYTE) || + type.equalsToText(CommonClassNames.JAVA_LANG_CHARACTER)) { + return; + } + } + else if (PsiType.INT.equals(type) || PsiType.SHORT.equals(type) || PsiType.BYTE.equals(type) || PsiType.CHAR.equals(type)) { + return; + } + } + if (!suggestEnumSwitches && type instanceof PsiClassType) { + final PsiClassType classType = (PsiClassType)type; + final PsiClass aClass = classType.resolve(); + if (aClass == null || aClass.isEnum()) { + return; + } + } + registerStatementError(statement, switchExpression); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/IndexOfReplaceableByContainsInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/IndexOfReplaceableByContainsInspection.java new file mode 100644 index 000000000000..bd7acc861042 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/IndexOfReplaceableByContainsInspection.java @@ -0,0 +1,272 @@ +/* + * Copyright 2005-2011 Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.siyeh.ig.migration; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ComparisonUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class IndexOfReplaceableByContainsInspection + extends BaseInspection { + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "indexof.replaceable.by.contains.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final PsiBinaryExpression expression = (PsiBinaryExpression)infos[0]; + final PsiExpression lhs = expression.getLOperand(); + final String text; + if (lhs instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression callExpression = + (PsiMethodCallExpression)lhs; + text = createContainsExpressionText(callExpression, false, + expression.getOperationTokenType()); + } + else { + final PsiMethodCallExpression callExpression = + (PsiMethodCallExpression)expression.getROperand(); + assert callExpression != null; + text = createContainsExpressionText(callExpression, true, + expression.getOperationTokenType()); + } + return InspectionGadgetsBundle.message( + "expression.can.be.replaced.problem.descriptor", text); + } + + @Override + @Nullable + protected InspectionGadgetsFix buildFix(Object... infos) { + return new IndexOfReplaceableByContainsFix(); + } + + private static class IndexOfReplaceableByContainsFix + extends InspectionGadgetsFix { + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) + throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + if (!(element instanceof PsiBinaryExpression)) { + return; + } + final PsiBinaryExpression expression = + (PsiBinaryExpression)element; + final PsiExpression lhs = expression.getLOperand(); + final PsiExpression rhs = expression.getROperand(); + final String newExpressionText; + if (lhs instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression callExpression = + (PsiMethodCallExpression)lhs; + newExpressionText = + createContainsExpressionText(callExpression, false, + expression.getOperationTokenType()); + } + else if (rhs instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression callExpression = + (PsiMethodCallExpression)rhs; + newExpressionText = + createContainsExpressionText(callExpression, true, + expression.getOperationTokenType()); + } + else { + return; + } + replaceExpression(expression, newExpressionText); + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message( + "replace.indexof.with.contains.quickfix"); + } + + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + } + + static String createContainsExpressionText( + @NotNull PsiMethodCallExpression call, + boolean flipped, IElementType tokenType) { + final PsiReferenceExpression methodExpression = + call.getMethodExpression(); + final PsiExpression qualifierExpression = + methodExpression.getQualifierExpression(); + final String qualifierText; + if (qualifierExpression == null) { + qualifierText = ""; + } + else { + qualifierText = qualifierExpression.getText(); + } + final PsiExpressionList argumentList = call.getArgumentList(); + final PsiExpression expression = argumentList.getExpressions()[0]; + @NonNls final String newExpressionText = + qualifierText + ".contains(" + expression.getText() + ')'; + if (tokenType.equals(JavaTokenType.EQEQ)) { + return '!' + newExpressionText; + } + else if (!flipped && (tokenType.equals(JavaTokenType.LT) || + tokenType.equals(JavaTokenType.LE))) { + return '!' + newExpressionText; + } + else if (flipped && (tokenType.equals(JavaTokenType.GT) || + tokenType.equals(JavaTokenType.GE))) { + return '!' + newExpressionText; + } + return newExpressionText; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new IndexOfReplaceableByContainsVisitor(); + } + + private static class IndexOfReplaceableByContainsVisitor + extends BaseInspectionVisitor { + + @Override + public void visitBinaryExpression( + PsiBinaryExpression expression) { + if (!PsiUtil.isLanguageLevel5OrHigher(expression)) { + return; + } + super.visitBinaryExpression(expression); + final PsiExpression rhs = expression.getROperand(); + if (rhs == null) { + return; + } + if (!ComparisonUtils.isComparison(expression)) { + return; + } + final PsiExpression lhs = expression.getLOperand(); + if (lhs instanceof PsiMethodCallExpression) { + if (canBeReplacedByContains(lhs, rhs, false, + expression.getOperationTokenType())) { + registerError(expression, expression); + } + } + else if (rhs instanceof PsiMethodCallExpression) { + if (canBeReplacedByContains(rhs, lhs, true, + expression.getOperationTokenType())) { + registerError(expression, expression); + } + } + } + + private static boolean canBeReplacedByContains( + PsiExpression lhs, + PsiExpression rhs, boolean flipped, IElementType tokenType) { + final PsiMethodCallExpression callExpression = + (PsiMethodCallExpression)lhs; + if (!isIndexOfCall(callExpression)) { + return false; + } + final Object object = + ExpressionUtils.computeConstantExpression(rhs); + if (!(object instanceof Integer)) { + return false; + } + final Integer integer = (Integer)object; + final int constant = integer.intValue(); + if (flipped) { + if (constant == -1 && (JavaTokenType.NE.equals(tokenType) || + JavaTokenType.LT.equals(tokenType) || + JavaTokenType.EQEQ.equals(tokenType) || + JavaTokenType.GE.equals(tokenType))) { + return true; + } + else if (constant == 0 && + (JavaTokenType.LE.equals(tokenType) || + JavaTokenType.GT.equals(tokenType))) { + return true; + } + } + else { + if (constant == -1 && (JavaTokenType.NE.equals(tokenType) || + JavaTokenType.GT.equals(tokenType) || + JavaTokenType.EQEQ.equals(tokenType) || + JavaTokenType.LE.equals(tokenType))) { + return true; + } + else if (constant == 0 && + (JavaTokenType.GE.equals(tokenType) || + JavaTokenType.LT.equals(tokenType))) { + return true; + } + } + return false; + } + + private static boolean isIndexOfCall( + @NotNull PsiMethodCallExpression expression) { + final PsiReferenceExpression methodExpression = + expression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.INDEX_OF.equals(methodName)) { + return false; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 1) { + return false; + } + final PsiType argumentType = arguments[0].getType(); + if (argumentType == null || + !argumentType.equalsToText( + CommonClassNames.JAVA_LANG_STRING)) { + return false; + } + final PsiExpression qualifier = + methodExpression.getQualifierExpression(); + if (qualifier == null) { + return false; + } + final PsiType qualifierType = qualifier.getType(); + return qualifierType != null && + qualifierType.equalsToText(CommonClassNames.JAVA_LANG_STRING); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/MethodCanBeVariableArityMethodInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/MethodCanBeVariableArityMethodInspection.java new file mode 100644 index 000000000000..ef1591864c0e --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/MethodCanBeVariableArityMethodInspection.java @@ -0,0 +1,158 @@ +/* + * Copyright 2011-2013 Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.LibraryUtil; +import com.siyeh.ig.psiutils.MethodUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class MethodCanBeVariableArityMethodInspection extends BaseInspection { + + @SuppressWarnings({"PublicField"}) + public boolean ignoreByteAndShortArrayParameters = false; + + @SuppressWarnings("PublicField") + public boolean ignoreOverridingMethods = false; + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("method.can.be.variable.arity.method.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("method.can.be.variable.arity.method.problem.descriptor"); + } + + @Override + public JComponent createOptionsPanel() { + final MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this); + panel.addCheckbox(InspectionGadgetsBundle.message("method.can.be.variable.arity.method.ignore.byte.short.option"), + "ignoreByteAndShortArrayParameters"); + panel.addCheckbox(InspectionGadgetsBundle.message("ignore.methods.overriding.super.method"), + "ignoreOverridingMethods"); + return panel; + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new MethodCanBeVariableArityMethodFix(); + } + + private static class MethodCanBeVariableArityMethodFix extends InspectionGadgetsFix { + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + @NotNull + @Override + public String getName() { + return InspectionGadgetsBundle.message("convert.to.variable.arity.method.quickfix"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement(); + final PsiElement parent = element.getParent(); + if (!(parent instanceof PsiMethod)) { + return; + } + final PsiMethod method = (PsiMethod)parent; + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() == 0) { + return; + } + final PsiParameter[] parameters = parameterList.getParameters(); + final PsiParameter lastParameter = parameters[parameters.length - 1]; + final PsiType type = lastParameter.getType(); + if (!(type instanceof PsiArrayType)) { + return; + } + final PsiArrayType arrayType = (PsiArrayType)type; + final PsiType componentType = arrayType.getComponentType(); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiTypeElement newTypeElement = factory.createTypeElementFromText(componentType.getCanonicalText() + "...", method); + final PsiTypeElement typeElement = lastParameter.getTypeElement(); + if (typeElement != null) { + typeElement.replace(newTypeElement); + } + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MethodCanBeVariableArityMethodVisitor(); + } + + private class MethodCanBeVariableArityMethodVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(PsiMethod method) { + if (!PsiUtil.isLanguageLevel5OrHigher(method)) { + return; + } + super.visitMethod(method); + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() == 0) { + return; + } + final PsiParameter[] parameters = parameterList.getParameters(); + final PsiParameter lastParameter = parameters[parameters.length - 1]; + final PsiType type = lastParameter.getType(); + if (!(type instanceof PsiArrayType)) { + return; + } + if (type instanceof PsiEllipsisType) { + return; + } + final PsiArrayType arrayType = (PsiArrayType)type; + final PsiType componentType = arrayType.getComponentType(); + if (componentType instanceof PsiArrayType) { + // don't report when it is multidimensional array + return; + } + if (ignoreByteAndShortArrayParameters) { + if (PsiType.BYTE.equals(componentType) || PsiType.SHORT.equals(componentType)) { + return; + } + } + if (LibraryUtil.isOverrideOfLibraryMethod(method)) { + return; + } + if (ignoreOverridingMethods && MethodUtils.hasSuper(method)) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/RawUseOfParameterizedTypeInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/RawUseOfParameterizedTypeInspection.java new file mode 100644 index 000000000000..4ff81ab84798 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/RawUseOfParameterizedTypeInspection.java @@ -0,0 +1,189 @@ +/* + * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; +import com.intellij.lang.java.JavaLanguage; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.MethodUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class RawUseOfParameterizedTypeInspection extends BaseInspection { + + @SuppressWarnings("PublicField") public boolean ignoreObjectConstruction = true; + + @SuppressWarnings("PublicField") public boolean ignoreTypeCasts = false; + + @SuppressWarnings("PublicField") public boolean ignoreUncompilable = false; + + @SuppressWarnings("PublicField") public boolean ignoreParametersOfOverridingMethods = false; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("raw.use.of.parameterized.type.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("raw.use.of.parameterized.type.problem.descriptor"); + } + + @Override + @Nullable + public JComponent createOptionsPanel() { + final MultipleCheckboxOptionsPanel optionsPanel = new MultipleCheckboxOptionsPanel(this); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message("raw.use.of.parameterized.type.ignore.new.objects.option"), + "ignoreObjectConstruction"); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message("raw.use.of.parameterized.type.ignore.type.casts.option"), + "ignoreTypeCasts"); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message("raw.use.of.parameterized.type.ignore.uncompilable.option"), + "ignoreUncompilable"); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message("raw.use.of.parameterized.type.ignore.overridden.parameter.option"), + "ignoreParametersOfOverridingMethods"); + return optionsPanel; + } + + @Override + public String getAlternativeID() { + return "rawtypes"; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new RawUseOfParameterizedTypeVisitor(); + } + + private class RawUseOfParameterizedTypeVisitor extends BaseInspectionVisitor { + + @Override + public void visitNewExpression(@NotNull PsiNewExpression expression) { + if (!hasNeededLanguageLevel(expression)) { + return; + } + super.visitNewExpression(expression); + if (ignoreObjectConstruction) { + return; + } + if (ignoreUncompilable && (expression.getArrayInitializer() != null || expression.getArrayDimensions().length > 0)) { + //array creation can (almost) never be generic + return; + } + final PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference(); + checkReferenceElement(classReference); + } + + @Override + public void visitTypeElement(@NotNull PsiTypeElement typeElement) { + if (!hasNeededLanguageLevel(typeElement)) { + return; + } + final PsiType type = typeElement.getType(); + if (!(type instanceof PsiClassType)) { + return; + } + super.visitTypeElement(typeElement); + final PsiElement parent = PsiTreeUtil.skipParentsOfType(typeElement, PsiTypeElement.class); + if (parent instanceof PsiInstanceOfExpression || parent instanceof PsiClassObjectAccessExpression) { + return; + } + if (ignoreTypeCasts && parent instanceof PsiTypeCastExpression) { + return; + } + if (PsiTreeUtil.getParentOfType(typeElement, PsiComment.class) != null) { + return; + } + final PsiAnnotationMethod annotationMethod = + PsiTreeUtil.getParentOfType(typeElement, PsiAnnotationMethod.class, true, PsiClass.class); + if (ignoreUncompilable && annotationMethod != null) { + // type of class type parameter cannot be parameterized if annotation method has default value + final PsiAnnotationMemberValue defaultValue = annotationMethod.getDefaultValue(); + if (defaultValue != null && parent != annotationMethod) { + return; + } + } + if (parent instanceof PsiParameter && ignoreParametersOfOverridingMethods) { + final PsiParameter parameter = (PsiParameter)parent; + final PsiElement declarationScope = parameter.getDeclarationScope(); + if (declarationScope instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)declarationScope; + if (MethodUtils.hasSuper(method)) { + return; + } + } + } + final PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement(); + checkReferenceElement(referenceElement); + } + + @Override + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + if (!hasNeededLanguageLevel(reference)) { + return; + } + super.visitReferenceElement(reference); + final PsiElement referenceParent = reference.getParent(); + if (!(referenceParent instanceof PsiReferenceList)) { + return; + } + final PsiReferenceList referenceList = (PsiReferenceList)referenceParent; + final PsiElement listParent = referenceList.getParent(); + if (!(listParent instanceof PsiClass)) { + return; + } + checkReferenceElement(reference); + } + + private void checkReferenceElement(PsiJavaCodeReferenceElement reference) { + if (reference == null) { + return; + } + final PsiType[] typeParameters = reference.getTypeParameters(); + if (typeParameters.length > 0) { + return; + } + final PsiElement element = reference.resolve(); + if (!(element instanceof PsiClass)) { + return; + } + final PsiClass aClass = (PsiClass)element; + final PsiElement qualifier = reference.getQualifier(); + if (qualifier instanceof PsiJavaCodeReferenceElement) { + final PsiJavaCodeReferenceElement qualifierReference = (PsiJavaCodeReferenceElement)qualifier; + if (!aClass.hasModifierProperty(PsiModifier.STATIC) && !aClass.isInterface() && !aClass.isEnum()) { + checkReferenceElement(qualifierReference); + } + } + if (!aClass.hasTypeParameters()) { + return; + } + registerError(reference); + } + + private boolean hasNeededLanguageLevel(PsiElement element) { + return element.getLanguage().isKindOf(JavaLanguage.INSTANCE) && PsiUtil.isLanguageLevel5OrHigher(element); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/StringBufferReplaceableByStringBuilderInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/StringBufferReplaceableByStringBuilderInspection.java new file mode 100644 index 000000000000..33b1a947acc8 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/StringBufferReplaceableByStringBuilderInspection.java @@ -0,0 +1,214 @@ +/* + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.TypeUtils; +import com.siyeh.ig.psiutils.VariableAccessUtils; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class StringBufferReplaceableByStringBuilderInspection extends BaseInspection { + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + @NotNull + public String getID() { + return "StringBufferMayBeStringBuilder"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.builder.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.builder.problem.descriptor"); + } + + @Override + public InspectionGadgetsFix buildFix(Object... infos) { + return new StringBufferMayBeStringBuilderFix(); + } + + @Nullable + private static PsiNewExpression getNewStringBuffer(PsiExpression expression) { + if (expression == null) { + return null; + } + else if (expression instanceof PsiNewExpression) { + return (PsiNewExpression)expression; + } + else if (expression instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + @NonNls final String methodName = methodExpression.getReferenceName(); + if (!"append".equals(methodName)) { + return null; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + return getNewStringBuffer(qualifier); + } + return null; + } + + private static class StringBufferMayBeStringBuilderFix extends InspectionGadgetsFix { + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.builder.replace.quickfix"); + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + public void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + final PsiElement parent = element.getParent(); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiClass stringBuilderClass = psiFacade.findClass(CommonClassNames.JAVA_LANG_STRING_BUILDER, element.getResolveScope()); + if (stringBuilderClass == null) { + return; + } + final PsiElementFactory factory = psiFacade.getElementFactory(); + final PsiJavaCodeReferenceElement stringBuilderClassReference = factory.createClassReferenceElement(stringBuilderClass); + final PsiClassType stringBuilderType = factory.createType(stringBuilderClass); + final PsiTypeElement stringBuilderTypeElement = factory.createTypeElement(stringBuilderType); + final PsiElement grandParent = parent.getParent(); + final PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement)grandParent; + final PsiElement[] declaredElements = declarationStatement.getDeclaredElements(); + for (PsiElement declaredElement : declaredElements) { + if (!(declaredElement instanceof PsiVariable)) { + continue; + } + replaceWithStringBuilder(stringBuilderClassReference, stringBuilderTypeElement, (PsiVariable)declaredElement); + } + } + + private static void replaceWithStringBuilder(PsiJavaCodeReferenceElement newClassReference, + PsiTypeElement newTypeElement, + PsiVariable variable) { + final PsiExpression initializer = getNewStringBuffer(variable.getInitializer()); + if (initializer == null) { + return; + } + final PsiNewExpression newExpression = (PsiNewExpression)initializer; + final PsiJavaCodeReferenceElement classReference = newExpression.getClassReference(); // no anonymous classes because StringBuffer is final + if (classReference == null) { + return; + } + final PsiTypeElement typeElement = variable.getTypeElement(); + if (typeElement != null && typeElement.getParent() == variable) { + typeElement.replace(newTypeElement); + } + classReference.replace(newClassReference); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new StringBufferReplaceableByStringBuilderVisitor(); + } + + private static class StringBufferReplaceableByStringBuilderVisitor extends BaseInspectionVisitor { + + private static final Set excludes = new HashSet(Arrays.asList(CommonClassNames.JAVA_LANG_STRING_BUILDER, + CommonClassNames.JAVA_LANG_STRING_BUFFER)); + + @Override + public void visitDeclarationStatement(PsiDeclarationStatement statement) { + if (!PsiUtil.isLanguageLevel5OrHigher(statement)) { + return; + } + super.visitDeclarationStatement(statement); + final PsiElement[] declaredElements = statement.getDeclaredElements(); + if (declaredElements.length == 0) { + return; + } + for (PsiElement declaredElement : declaredElements) { + if (!(declaredElement instanceof PsiLocalVariable)) { + return; + } + final PsiLocalVariable variable = (PsiLocalVariable)declaredElement; + final PsiElement context = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class, true, PsiClass.class); + if (!isReplaceableStringBuffer(variable, context)) { + return; + } + } + final PsiLocalVariable firstVariable = (PsiLocalVariable)declaredElements[0]; + registerVariableError(firstVariable); + } + + private static boolean isReplaceableStringBuffer(PsiVariable variable, PsiElement context) { + if (context == null) { + return false; + } + final PsiType type = variable.getType(); + if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUFFER, type)) { + return false; + } + final PsiExpression initializer = variable.getInitializer(); + if (initializer == null) { + return false; + } + if (getNewStringBuffer(initializer) == null) { + return false; + } + if (VariableAccessUtils.variableIsAssigned(variable, context)) { + return false; + } + if (VariableAccessUtils.variableIsAssignedFrom(variable, context)) { + return false; + } + if (VariableAccessUtils.variableIsReturned(variable, context, true)) { + return false; + } + if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, excludes, context, true)) { + return false; + } + if (VariableAccessUtils.variableIsUsedInInnerClass(variable, context)) { + return false; + } + return true; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java new file mode 100644 index 000000000000..804a290d4d06 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryFinallyCanBeTryWithResourcesInspection.java @@ -0,0 +1,568 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Bas Leijdekkers + */ +public class TryFinallyCanBeTryWithResourcesInspection extends BaseInspection { + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("try.finally.can.be.try.with.resources.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("try.finally.can.be.try.with.resources.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new TryFinallyCanBeTryWithResourcesFix(); + } + + private static class TryFinallyCanBeTryWithResourcesFix extends InspectionGadgetsFix { + + public TryFinallyCanBeTryWithResourcesFix() {} + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("try.finally.can.be.try.with.resources.quickfix"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + final PsiElement parent = element.getParent(); + if (!(parent instanceof PsiTryStatement)) { + return; + } + final PsiTryStatement tryStatement = (PsiTryStatement)parent; + final PsiCodeBlock tryBlock = tryStatement.getTryBlock(); + if (tryBlock == null) { + return; + } + final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); + if (finallyBlock == null) { + return; + } + final PsiElement[] tryBlockChildren = tryBlock.getChildren(); + final Set variables = new HashSet(); + for (PsiLocalVariable variable : collectVariables(tryStatement)) { + if (!isVariableUsedOutsideContext(variable, tryBlock)) { + variables.add(variable); + } + } + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + @NonNls final StringBuilder newTryStatementText = new StringBuilder("try ("); + final Set unwantedChildren = new HashSet(2); + boolean separator = false; + for (PsiLocalVariable variable : variables) { + final boolean hasInitializer; + final PsiExpression initializer = variable.getInitializer(); + if (initializer == null) { + hasInitializer = false; + } + else { + final PsiType type = initializer.getType(); + hasInitializer = !PsiType.NULL.equals(type); + } + if (separator) { + newTryStatementText.append(';'); + } + newTryStatementText.append(variable.getTypeElement().getText()).append(' ').append(variable.getName()).append('='); + if (hasInitializer) { + newTryStatementText.append(initializer.getText()); + } + else { + final int index = findInitialization(tryBlockChildren, variable, hasInitializer); + if (index < 0) { + return; + } + unwantedChildren.add(Integer.valueOf(index)); + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)tryBlockChildren[index]; + if (expressionStatement.getNextSibling() instanceof PsiWhiteSpace) { + unwantedChildren.add(Integer.valueOf(index + 1)); + } + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expressionStatement.getExpression(); + final PsiExpression rhs = assignmentExpression.getRExpression(); + if (rhs == null) { + return; + } + newTryStatementText.append(rhs.getText()); + } + separator = true; + } + newTryStatementText.append(") {"); + final int tryBlockStatementsLength = tryBlockChildren.length - 1; + for (int i = 1; i < tryBlockStatementsLength; i++) { + final PsiElement child = tryBlockChildren[i]; + if (unwantedChildren.contains(Integer.valueOf(i))) { + continue; + } + newTryStatementText.append(child.getText()); + } + newTryStatementText.append('}'); + final PsiCatchSection[] catchSections = tryStatement.getCatchSections(); + for (PsiCatchSection catchSection : catchSections) { + newTryStatementText.append(catchSection.getText()); + } + final PsiElement[] finallyChildren = finallyBlock.getChildren(); + boolean appended = false; + final int finallyChildrenLength = finallyChildren.length - 1; + final List savedComments = new ArrayList(); + for (int i = 1; i < finallyChildrenLength; i++) { + final PsiElement child = finallyChildren[i]; + if (isCloseStatement(child, variables)) { + continue; + } + if (!appended) { + if (child instanceof PsiComment) { + final PsiElement prevSibling = child.getPrevSibling(); + if (prevSibling instanceof PsiWhiteSpace) { + savedComments.add(prevSibling); + } + savedComments.add(child); + } + else if (!(child instanceof PsiWhiteSpace)) { + newTryStatementText.append(" finally {"); + for (PsiElement savedComment : savedComments) { + newTryStatementText.append(savedComment.getText()); + } + newTryStatementText.append(child.getText()); + appended = true; + } + } + else { + newTryStatementText.append(child.getText()); + } + } + if (appended) { + newTryStatementText.append('}'); + } + for (PsiLocalVariable variable : variables) { + variable.delete(); + } + if (!appended) { + final int savedCommentsSize = savedComments.size(); + final PsiElement parent1 = tryStatement.getParent(); + for (int i = savedCommentsSize - 1; i >= 0; i--) { + final PsiElement savedComment = savedComments.get(i); + parent1.addAfter(savedComment, tryStatement); + } + } + final PsiStatement newTryStatement = factory.createStatementFromText(newTryStatementText.toString(), element); + tryStatement.replace(newTryStatement); + } + + private static boolean isCloseStatement(PsiElement element, Set variables) { + if (element instanceof PsiExpressionStatement) { + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)element; + final PsiExpression expression = expressionStatement.getExpression(); + if (!(expression instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.CLOSE.equals(methodName)) { + return false; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (!(qualifier instanceof PsiReferenceExpression)) { + return false; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + if (!(target instanceof PsiLocalVariable)) { + return false; + } + final PsiLocalVariable variable = (PsiLocalVariable)target; + return variables.contains(variable); + } + else if (element instanceof PsiIfStatement) { + final PsiIfStatement ifStatement = (PsiIfStatement)element; + if (ifStatement.getElseBranch() != null) { + return false; + } + final PsiExpression condition = ifStatement.getCondition(); + if (!(condition instanceof PsiBinaryExpression)) { + return false; + } + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)condition; + final IElementType tokenType = binaryExpression.getOperationTokenType(); + if (!JavaTokenType.NE.equals(tokenType)) { + return false; + } + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + if (rhs == null) { + return false; + } + final PsiElement variable; + if (PsiType.NULL.equals(rhs.getType())) { + if (!(lhs instanceof PsiReferenceExpression)) { + return false; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; + variable = referenceExpression.resolve(); + if (!(variable instanceof PsiLocalVariable)) { + return false; + } + } + else if (PsiType.NULL.equals(lhs.getType())) { + if (!(rhs instanceof PsiReferenceExpression)) { + return false; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)rhs; + variable = referenceExpression.resolve(); + if (!(variable instanceof PsiLocalVariable)) { + return false; + } + } + else { + return false; + } + final PsiStatement thenBranch = ifStatement.getThenBranch(); + if (thenBranch instanceof PsiExpressionStatement) { + return isCloseStatement(thenBranch, variables); + } + else if (thenBranch instanceof PsiBlockStatement) { + final PsiBlockStatement blockStatement = (PsiBlockStatement)thenBranch; + final PsiCodeBlock codeBlock = blockStatement.getCodeBlock(); + final PsiStatement[] statements = codeBlock.getStatements(); + return statements.length == 1 && isCloseStatement(statements[0], variables); + } + else { + return false; + } + } + else { + return false; + } + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new TryFinallyCanBeTryWithResourcesVisitor(); + } + + private static class TryFinallyCanBeTryWithResourcesVisitor extends BaseInspectionVisitor { + + @Override + public void visitTryStatement(PsiTryStatement tryStatement) { + super.visitTryStatement(tryStatement); + if (!PsiUtil.isLanguageLevel7OrHigher(tryStatement)) { + return; + } + final PsiResourceList resourceList = tryStatement.getResourceList(); + if (resourceList != null) { + return; + } + final PsiCodeBlock tryBlock = tryStatement.getTryBlock(); + if (tryBlock == null) { + return; + } + final List variables = collectVariables(tryStatement); + if (variables.isEmpty()) { + return; + } + final PsiStatement[] tryBlockStatements = tryBlock.getStatements(); + boolean found = false; + for (PsiVariable variable : variables) { + final boolean hasInitializer; + final PsiExpression initializer = variable.getInitializer(); + if (initializer == null) { + hasInitializer = false; + } + else { + final PsiType type = initializer.getType(); + hasInitializer = !PsiType.NULL.equals(type); + } + final int index = findInitialization(tryBlockStatements, variable, hasInitializer); + if (index >= 0 ^ hasInitializer) { + if (isVariableUsedOutsideContext(variable, tryBlock)) { + continue; + } + found = true; + break; + } + } + if (!found) { + return; + } + registerStatementError(tryStatement); + } + } + + static boolean isVariableUsedOutsideContext(PsiVariable variable, PsiElement context) { + final VariableUsedOutsideContextVisitor visitor = new VariableUsedOutsideContextVisitor(variable, context); + final PsiElement declarationScope = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); + if (declarationScope == null) { + return true; + } + declarationScope.accept(visitor); + return visitor.variableIsUsed(); + } + + static List collectVariables(PsiTryStatement tryStatement) { + final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); + if (finallyBlock == null) { + return Collections.EMPTY_LIST; + } + final PsiStatement[] statements = finallyBlock.getStatements(); + if (statements.length == 0) { + return Collections.EMPTY_LIST; + } + final List variables = new ArrayList(); + for (PsiStatement statement : statements) { + final PsiLocalVariable variable = findAutoCloseableVariable(statement); + if (variable != null) { + variables.add(variable); + } + } + return variables; + } + + @Nullable + static PsiLocalVariable findAutoCloseableVariable(PsiStatement statement) { + if (statement instanceof PsiIfStatement) { + final PsiIfStatement ifStatement = (PsiIfStatement)statement; + if (ifStatement.getElseBranch() != null) { + return null; + } + final PsiExpression condition = ifStatement.getCondition(); + if (!(condition instanceof PsiBinaryExpression)) { + return null; + } + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)condition; + final IElementType tokenType = binaryExpression.getOperationTokenType(); + if (!JavaTokenType.NE.equals(tokenType)) { + return null; + } + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + if (rhs == null) { + return null; + } + final PsiElement variable; + if (PsiType.NULL.equals(rhs.getType())) { + if (!(lhs instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; + variable = referenceExpression.resolve(); + if (!(variable instanceof PsiLocalVariable)) { + return null; + } + } + else if (PsiType.NULL.equals(lhs.getType())) { + if (!(rhs instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)rhs; + variable = referenceExpression.resolve(); + if (!(variable instanceof PsiLocalVariable)) { + return null; + } + } + else { + return null; + } + final PsiStatement thenBranch = ifStatement.getThenBranch(); + final PsiLocalVariable resourceVariable; + if (thenBranch instanceof PsiExpressionStatement) { + resourceVariable = findAutoCloseableVariable(thenBranch); + } + else if (thenBranch instanceof PsiBlockStatement) { + final PsiBlockStatement blockStatement = (PsiBlockStatement)thenBranch; + final PsiCodeBlock codeBlock = blockStatement.getCodeBlock(); + final PsiStatement[] statements = codeBlock.getStatements(); + if (statements.length != 1) { + return null; + } + resourceVariable = findAutoCloseableVariable(statements[0]); + } + else { + return null; + } + if (variable.equals(resourceVariable)) { + return resourceVariable; + } + } + else if (statement instanceof PsiExpressionStatement) { + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)statement; + final PsiExpression expression = expressionStatement.getExpression(); + if (!(expression instanceof PsiMethodCallExpression)) { + return null; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.CLOSE.equals(methodName)) { + return null; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (!(qualifier instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + if (!(target instanceof PsiLocalVariable) || target instanceof PsiResourceVariable) { + return null; + } + final PsiLocalVariable variable = (PsiLocalVariable)target; + if (!isAutoCloseable(variable)) { + return null; + } + return variable; + } + return null; + } + + private static boolean isAutoCloseable(PsiVariable variable) { + final PsiType type = variable.getType(); + if (!(type instanceof PsiClassType)) { + return false; + } + final PsiClassType classType = (PsiClassType)type; + final PsiClass aClass = classType.resolve(); + return aClass != null && InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE); + } + + static int findInitialization(PsiElement[] elements, PsiVariable variable, + boolean hasInitializer) { + int result = -1; + final int statementsLength = elements.length; + for (int i = 0; i < statementsLength; i++) { + final PsiElement element = elements[i]; + if (!(element instanceof PsiExpressionStatement)) { + continue; + } + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)element; + final PsiExpression expression = expressionStatement.getExpression(); + if (!(expression instanceof PsiAssignmentExpression)) { + continue; + } + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression; + final PsiExpression lhs = assignmentExpression.getLExpression(); + if (!(lhs instanceof PsiReferenceExpression)) { + continue; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; + final PsiElement target = referenceExpression.resolve(); + if (variable.equals(target)) { + if (result >= 0 && !hasInitializer) { + return -1; + } + result = i; + } + } + return result; + } + + static class VariableUsedOutsideContextVisitor extends JavaRecursiveElementVisitor { + + private boolean used = false; + @NotNull private final PsiVariable variable; + private final PsiElement skipContext; + + public VariableUsedOutsideContextVisitor(@NotNull PsiVariable variable, PsiElement skipContext) { + this.variable = variable; + this.skipContext = skipContext; + } + + @Override + public void visitElement(@NotNull PsiElement element) { + if (element.equals(skipContext)) { + return; + } + if (used) { + return; + } + super.visitElement(element); + } + + @Override + public void visitReferenceExpression(@NotNull PsiReferenceExpression referenceExpression) { + if (used) { + return; + } + super.visitReferenceExpression(referenceExpression); + final PsiElement target = referenceExpression.resolve(); + if (target == null) { + return; + } + if (target.equals(variable) && !isCloseMethodCalled(referenceExpression)) { + used = true; + } + } + + private static boolean isCloseMethodCalled(PsiReferenceExpression referenceExpression) { + final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(referenceExpression, PsiMethodCallExpression.class); + if (methodCallExpression == null) { + return false; + } + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + if (argumentList.getExpressions().length != 0) { + return false; + } + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String name = methodExpression.getReferenceName(); + return HardcodedMethodConstants.CLOSE.equals(name); + } + + public boolean variableIsUsed() { + return used; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java new file mode 100644 index 000000000000..532589663a57 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java @@ -0,0 +1,247 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.refactoring.extractMethod.InputVariables; +import com.intellij.refactoring.util.duplicates.DuplicatesFinder; +import com.intellij.refactoring.util.duplicates.Match; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author yole, Bas Leijdekkers + */ +public class TryWithIdenticalCatchesInspection extends BaseInspection { + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + final PsiType type = (PsiType)infos[1]; + return InspectionGadgetsBundle.message("try.with.identical.catches.problem.descriptor", type.getPresentableText()); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new TryWithIdenticalCatchesVisitor(); + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("try.with.identical.catches.display.name"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new CollapseCatchSectionsFix(((Integer)infos[0]).intValue()); + } + + private static class TryWithIdenticalCatchesVisitor extends BaseInspectionVisitor { + + @Override + public void visitTryStatement(PsiTryStatement statement) { + super.visitTryStatement(statement); + if (!PsiUtil.isLanguageLevel7OrHigher(statement)) { + return; + } + final PsiCatchSection[] catchSections = statement.getCatchSections(); + if (catchSections.length < 2) { + return; + } + final PsiParameter[] parameters = statement.getCatchBlockParameters(); + if (catchSections.length != parameters.length) { + return; + } + final boolean[] duplicates = new boolean[catchSections.length]; + for (int i = 0; i < catchSections.length - 1; i++) { + final PsiCatchSection catchSection = catchSections[i]; + final PsiCodeBlock catchBlock = catchSection.getCatchBlock(); + if (catchBlock == null) { + continue; + } + final PsiParameter parameter = catchSection.getParameter(); + if (parameter == null) { + continue; + } + final InputVariables inputVariables = new InputVariables(Collections.singletonList(parameter), + statement.getProject(), + new LocalSearchScope(catchBlock), + false); + final DuplicatesFinder finder = new DuplicatesFinder(new PsiElement[]{catchBlock}, + inputVariables, null, Collections.emptyList()); + for (int j = i + 1; j < catchSections.length; j++) { + if (duplicates[j]) { + continue; + } + final PsiCatchSection otherSection = catchSections[j]; + final PsiCodeBlock otherCatchBlock = otherSection.getCatchBlock(); + if (otherCatchBlock == null) { + continue; + } + final Match match = finder.isDuplicate(otherCatchBlock, true); + if (match == null || match.getReturnValue() != null) { + continue; + } + final List parameterValues = match.getParameterValues(parameter); + if (parameterValues != null && (parameterValues.size() != 1 || !(parameterValues.get(0) instanceof PsiReferenceExpression))) { + continue; + } + if (!canCollapse(parameters, i, j)) { + continue; + } + final PsiJavaToken rParenth = otherSection.getRParenth(); + if (rParenth != null) { + registerErrorAtOffset(otherSection, 0, rParenth.getStartOffsetInParent() + 1, Integer.valueOf(i), parameter.getType()); + } + duplicates[i] = true; + duplicates[j] = true; + } + } + } + + private static boolean canCollapse(PsiParameter[] parameters, int index1, int index2) { + if (index2 > index1) { + final PsiType type = parameters[index2].getType(); + for (int i = index1 + 1; i < index2; i++) { + final PsiType otherType = parameters[i].getType(); + if (TypeConversionUtil.isAssignable(type, otherType)) { + return false; + } + } + return true; + } + else { + final PsiType type = parameters[index1].getType(); + for (int i = index2 + 1; i < index1; i++) { + final PsiType otherType = parameters[i].getType(); + if (TypeConversionUtil.isAssignable(otherType, type)) { + return false; + } + } + return true; + } + } + } + + private static class CollapseCatchSectionsFix extends InspectionGadgetsFix { + + private final int myCollapseIntoIndex; + + public CollapseCatchSectionsFix(int collapseIntoIndex) { + myCollapseIntoIndex = collapseIntoIndex; + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("try.with.identical.catches.quickfix"); + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiCatchSection section = (PsiCatchSection)descriptor.getPsiElement(); + final PsiTryStatement tryStatement = (PsiTryStatement)section.getParent(); + final PsiCatchSection[] catchSections = tryStatement.getCatchSections(); + if (myCollapseIntoIndex >= catchSections.length) { + return; // something has gone stale + } + final PsiCatchSection collapseInto = catchSections[myCollapseIntoIndex]; + final PsiParameter parameter1 = collapseInto.getParameter(); + final PsiParameter parameter2 = section.getParameter(); + if (parameter1 == null || parameter2 == null) { + return; + } + final PsiType type1 = parameter1.getType(); + final PsiType type2 = parameter2.getType(); + if (TypeConversionUtil.isAssignable(type1, type2)) { + section.delete(); + return; + } + else if (TypeConversionUtil.isAssignable(type2, type1)) { + collapseInto.delete(); + return; + } + final List types = new ArrayList(); + collectDisjunctTypes(type1, types); + collectDisjunctTypes(type2, types); + final StringBuilder typeText = new StringBuilder(); + for (PsiType type : types) { + if (typeText.length() > 0) { + typeText.append(" | "); + } + typeText.append(type.getCanonicalText()); + } + final PsiTypeElement newTypeElement = + JavaPsiFacade.getElementFactory(project).createTypeElementFromText(typeText.toString(), tryStatement); + final PsiTypeElement typeElement = parameter1.getTypeElement(); + if (typeElement == null) { + return; + } + typeElement.replace(newTypeElement); + section.delete(); + } + + private static void collectDisjunctTypes(PsiType type, List out) { + if (type instanceof PsiDisjunctionType) { + final PsiDisjunctionType disjunctionType = (PsiDisjunctionType)type; + final List disjunctions = disjunctionType.getDisjunctions(); + for (PsiType disjunction : disjunctions) { + collectDisjunctTypes(disjunction, out); + } + return; + } + final int size = out.size(); + for (int i = 0; i < size; i++) { + final PsiType collectedType = out.get(i); + if (TypeConversionUtil.isAssignable(type, collectedType)) { + out.remove(i); + out.add(type); + return; + } else if (TypeConversionUtil.isAssignable(collectedType, type)) { + return; + } + } + out.add(type); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/UnnecessaryBoxingInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/UnnecessaryBoxingInspection.java new file mode 100644 index 000000000000..2b1551e15387 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/UnnecessaryBoxingInspection.java @@ -0,0 +1,336 @@ +/* + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.MethodCallUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public class UnnecessaryBoxingInspection extends BaseInspection { + + @NonNls static final Map boxedPrimitiveMap = new HashMap(8); + + static { + boxedPrimitiveMap.put(CommonClassNames.JAVA_LANG_INTEGER, "int"); + boxedPrimitiveMap.put(CommonClassNames.JAVA_LANG_SHORT, "short"); + boxedPrimitiveMap.put(CommonClassNames.JAVA_LANG_BOOLEAN, "boolean"); + boxedPrimitiveMap.put(CommonClassNames.JAVA_LANG_LONG, "long"); + boxedPrimitiveMap.put(CommonClassNames.JAVA_LANG_BYTE, "byte"); + boxedPrimitiveMap.put(CommonClassNames.JAVA_LANG_FLOAT, "float"); + boxedPrimitiveMap.put(CommonClassNames.JAVA_LANG_DOUBLE, "double"); + boxedPrimitiveMap.put(CommonClassNames.JAVA_LANG_CHARACTER, "char"); + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("unnecessary.boxing.display.name"); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("unnecessary.boxing.problem.descriptor"); + } + + @Override + public InspectionGadgetsFix buildFix(Object... infos) { + return new UnnecessaryBoxingFix(); + } + + private static class UnnecessaryBoxingFix extends InspectionGadgetsFix { + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("unnecessary.boxing.remove.quickfix"); + } + + @Override + public void doFix(@NotNull Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiCallExpression expression = (PsiCallExpression)descriptor.getPsiElement(); + final PsiType boxedType = expression.getType(); + if (boxedType == null) { + return; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + if (argumentList == null) { + return; + } + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 1) { + return; + } + final PsiExpression unboxedExpression = arguments[0]; + final PsiType unboxedType = unboxedExpression.getType(); + if (unboxedType == null) { + return; + } + final String cast = getCastString(unboxedType, boxedType); + if (cast == null) { + return; + } + final int precedence = ParenthesesUtils.getPrecedence(unboxedExpression); + if (cast.length() > 0 && precedence > ParenthesesUtils.TYPE_CAST_PRECEDENCE) { + replaceExpression(expression, cast + '(' + unboxedExpression.getText() + ')'); + } + else { + replaceExpression(expression, cast + unboxedExpression.getText()); + } + } + + @Nullable + private static String getCastString(@NotNull PsiType fromType, @NotNull PsiType toType) { + final String toTypeText = toType.getCanonicalText(); + final String fromTypeText = fromType.getCanonicalText(); + final String unboxedType = boxedPrimitiveMap.get(toTypeText); + if (unboxedType == null) { + return null; + } + if (fromTypeText.equals(unboxedType)) { + return ""; + } + else { + return '(' + unboxedType + ')'; + } + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UnnecessaryBoxingVisitor(); + } + + private static class UnnecessaryBoxingVisitor extends BaseInspectionVisitor { + + @Override + public void visitNewExpression(@NotNull PsiNewExpression expression) { + if (!PsiUtil.isLanguageLevel5OrHigher(expression)) { + return; + } + super.visitNewExpression(expression); + final PsiType constructorType = expression.getType(); + if (constructorType == null) { + return; + } + final String constructorTypeText = constructorType.getCanonicalText(); + if (!boxedPrimitiveMap.containsKey(constructorTypeText)) { + return; + } + final PsiMethod constructor = expression.resolveConstructor(); + if (constructor == null) { + return; + } + final PsiParameterList parameterList = constructor.getParameterList(); + if (parameterList.getParametersCount() != 1) { + return; + } + final PsiParameter[] parameters = parameterList.getParameters(); + final PsiParameter parameter = parameters[0]; + final PsiType parameterType = parameter.getType(); + final String parameterTypeText = parameterType.getCanonicalText(); + final String boxableConstructorType = boxedPrimitiveMap.get(constructorTypeText); + if (!boxableConstructorType.equals(parameterTypeText)) { + return; + } + if (!canBeUnboxed(expression)) { + return; + } + registerError(expression); + } + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + if (!PsiUtil.isLanguageLevel5OrHigher(expression)) { + return; + } + super.visitMethodCallExpression(expression); + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 1) { + return; + } + if (!(arguments[0].getType() instanceof PsiPrimitiveType)) { + return; + } + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + @NonNls + final String referenceName = methodExpression.getReferenceName(); + if (!"valueOf".equals(referenceName)) { + return; + } + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + if (!(qualifierExpression instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifierExpression; + final String canonicalText = referenceExpression.getCanonicalText(); + if (!boxedPrimitiveMap.containsKey(canonicalText)) { + return; + } + if (!canBeUnboxed(expression)) { + return; + } + registerError(expression); + } + + private static boolean canBeUnboxed(PsiExpression expression) { + PsiElement parent = expression.getParent(); + while (parent instanceof PsiParenthesizedExpression) { + parent = parent.getParent(); + } + if (parent instanceof PsiExpressionStatement || parent instanceof PsiReferenceExpression) { + return false; + } + else if (parent instanceof PsiTypeCastExpression) { + final PsiTypeCastExpression castExpression = (PsiTypeCastExpression)parent; + final PsiType castType = castExpression.getType(); + if (castType instanceof PsiClassType) { + final PsiClassType classType = (PsiClassType)castType; + final PsiClass aClass = classType.resolve(); + if (aClass instanceof PsiTypeParameter) { + return false; + } + } + } + else if (parent instanceof PsiConditionalExpression) { + final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)parent; + final PsiExpression thenExpression = conditionalExpression.getThenExpression(); + final PsiExpression elseExpression = conditionalExpression.getElseExpression(); + if (elseExpression == null || thenExpression == null) { + return false; + } + if (PsiTreeUtil.isAncestor(thenExpression, expression, false)) { + final PsiType type = elseExpression.getType(); + return type instanceof PsiPrimitiveType; + } + else if (PsiTreeUtil.isAncestor(elseExpression, expression, false)) { + final PsiType type = thenExpression.getType(); + return type instanceof PsiPrimitiveType; + } + else { + return true; + } + } + else if (parent instanceof PsiBinaryExpression) { + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)parent; + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + if (rhs == null) { + return false; + } + final PsiType rhsType = rhs.getType(); + if (rhsType == null) { + return false; + } + final PsiType lhsType = lhs.getType(); + if (lhsType == null) { + return false; + } + if (PsiTreeUtil.isAncestor(rhs, expression, false)) { + final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(rhsType); + return unboxedType != null && unboxedType.isAssignableFrom(lhsType); + } + else { + final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(lhsType); + return unboxedType != null && unboxedType.isAssignableFrom(rhsType); + } + } + final PsiMethodCallExpression containingMethodCallExpression = getParentMethodCallExpression(expression); + return containingMethodCallExpression == null || isSameMethodCalledWithoutBoxing(containingMethodCallExpression, expression); + } + + @Nullable + private static PsiMethodCallExpression getParentMethodCallExpression(@NotNull PsiElement expression) { + final PsiElement parent = expression.getParent(); + if (parent instanceof PsiParenthesizedExpression || parent instanceof PsiExpressionList) { + return getParentMethodCallExpression(parent); + } + else if (parent instanceof PsiMethodCallExpression) { + return (PsiMethodCallExpression)parent; + } + else { + return null; + } + } + + private static boolean isSameMethodCalledWithoutBoxing(@NotNull PsiMethodCallExpression methodCallExpression, + @NotNull PsiExpression boxingExpression) { + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + final PsiExpression[] expressions = argumentList.getExpressions(); + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiElement element = methodExpression.resolve(); + if (!(element instanceof PsiMethod)) { + return false; + } + final PsiMethod originalMethod = (PsiMethod)element; + final String name = originalMethod.getName(); + final PsiClass containingClass = originalMethod.getContainingClass(); + if (containingClass == null) { + return false; + } + final PsiType[] types = new PsiType[expressions.length]; + for (int i = 0; i < expressions.length; i++) { + final PsiExpression expression = expressions[i]; + final PsiType type = expression.getType(); + if (boxingExpression.equals(expression)) { + final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type); + if (unboxedType == null) { + return false; + } + types[i] = unboxedType; + } + else { + types[i] = type; + } + } + final PsiMethod[] methods = containingClass.findMethodsByName(name, true); + for (PsiMethod method : methods) { + if (!originalMethod.equals(method)) { + if (MethodCallUtils.isApplicable(method, PsiSubstitutor.EMPTY, types)) { + return false; + } + } + } + return true; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/UnnecessaryUnboxingInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/UnnecessaryUnboxingInspection.java new file mode 100644 index 000000000000..d3acf7d8d5c3 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/UnnecessaryUnboxingInspection.java @@ -0,0 +1,325 @@ +/* + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ComparisonUtils; +import com.siyeh.ig.psiutils.MethodCallUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public class UnnecessaryUnboxingInspection extends BaseInspection { + + @NonNls static final Map s_unboxingMethods = + new HashMap(8); + + static { + s_unboxingMethods.put(CommonClassNames.JAVA_LANG_INTEGER, "intValue"); + s_unboxingMethods.put(CommonClassNames.JAVA_LANG_SHORT, "shortValue"); + s_unboxingMethods.put(CommonClassNames.JAVA_LANG_BOOLEAN, "booleanValue"); + s_unboxingMethods.put(CommonClassNames.JAVA_LANG_LONG, "longValue"); + s_unboxingMethods.put(CommonClassNames.JAVA_LANG_BYTE, "byteValue"); + s_unboxingMethods.put(CommonClassNames.JAVA_LANG_FLOAT, "floatValue"); + s_unboxingMethods.put(CommonClassNames.JAVA_LANG_DOUBLE, "doubleValue"); + s_unboxingMethods.put(CommonClassNames.JAVA_LANG_CHARACTER, "charValue"); + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "unnecessary.unboxing.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "unnecessary.unboxing.problem.descriptor"); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UnnecessaryUnboxingVisitor(); + } + + @Override + public InspectionGadgetsFix buildFix(Object... infos) { + return new UnnecessaryUnboxingFix(); + } + + private static class UnnecessaryUnboxingFix extends InspectionGadgetsFix { + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message( + "unnecessary.unboxing.remove.quickfix"); + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + public void doFix(Project project, ProblemDescriptor descriptor) + throws IncorrectOperationException { + final PsiMethodCallExpression methodCall = + (PsiMethodCallExpression)descriptor.getPsiElement(); + final PsiReferenceExpression methodExpression = + methodCall.getMethodExpression(); + final PsiExpression qualifier = + methodExpression.getQualifierExpression(); + final PsiExpression strippedQualifier = + ParenthesesUtils.stripParentheses(qualifier); + if (strippedQualifier == null) { + return; + } + if (strippedQualifier instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)strippedQualifier; + final PsiElement element = referenceExpression.resolve(); + if (element instanceof PsiField) { + final PsiField field = (PsiField)element; + final PsiClass containingClass = field.getContainingClass(); + if (containingClass == null) { + return; + } + final String classname = containingClass.getQualifiedName(); + if (CommonClassNames.JAVA_LANG_BOOLEAN.equals(classname)) { + @NonNls final String name = field.getName(); + if ("TRUE".equals(name)) { + replaceExpression(methodCall, "true"); + return; + } + else if ("FALSE".equals(name)) { + replaceExpression(methodCall, "false"); + return; + } + } + } + } + final String strippedQualifierText = strippedQualifier.getText(); + replaceExpression(methodCall, strippedQualifierText); + } + } + + private static class UnnecessaryUnboxingVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression( + @NotNull PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + if (!PsiUtil.isLanguageLevel5OrHigher(expression)) { + return; + } + if (!isUnboxingExpression(expression)) { + return; + } + final PsiExpression containingExpression = + getContainingExpression(expression); + if (containingExpression instanceof PsiTypeCastExpression) { + return; + } + if (isPossibleObjectComparison(expression, containingExpression)) { + return; + } + if (containingExpression instanceof PsiConditionalExpression) { + final PsiConditionalExpression conditionalExpression = + (PsiConditionalExpression)containingExpression; + final PsiExpression thenExpression = + conditionalExpression.getThenExpression(); + if (thenExpression == null) { + return; + } + final PsiExpression elseExpression = + conditionalExpression.getElseExpression(); + if (elseExpression == null) { + return; + } + if (PsiTreeUtil.isAncestor(thenExpression, expression, false)) { + final PsiType type = elseExpression.getType(); + if (!(type instanceof PsiPrimitiveType)) { + return; + } + } + else if (PsiTreeUtil.isAncestor(elseExpression, expression, + false)) { + final PsiType type = thenExpression.getType(); + if (!(type instanceof PsiPrimitiveType)) { + return; + } + } + } + else if (containingExpression instanceof PsiCallExpression) { + final PsiCallExpression methodCallExpression = + (PsiCallExpression)containingExpression; + if (!isSameMethodCalledWithoutUnboxing(methodCallExpression, + expression)) { + return; + } + } + registerError(expression); + } + + private static boolean isPossibleObjectComparison( + PsiMethodCallExpression expression, + PsiExpression containingExpression) { + if (!(containingExpression instanceof PsiBinaryExpression)) { + return false; + } + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression)containingExpression; + if (!ComparisonUtils.isEqualityComparison(binaryExpression)) { + return false; + } + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + if (rhs == null) { + return true; + } + if (expression == lhs) { + if (!(rhs.getType() instanceof PsiPrimitiveType) || + isUnboxingExpression(rhs)) { + return true; + } + } + if (expression == rhs) { + if (!(lhs.getType() instanceof PsiPrimitiveType) || + isUnboxingExpression(lhs)) { + return true; + } + } + return false; + } + + private static boolean isUnboxingExpression( + PsiExpression expression) { + if (!(expression instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression methodCallExpression = + (PsiMethodCallExpression)expression; + final PsiReferenceExpression methodExpression = + methodCallExpression.getMethodExpression(); + final PsiExpression qualifier = + methodExpression.getQualifierExpression(); + if (qualifier == null) { + return false; + } + final PsiType qualifierType = qualifier.getType(); + if (qualifierType == null) { + return false; + } + final String qualifierTypeName = qualifierType.getCanonicalText(); + if (!s_unboxingMethods.containsKey(qualifierTypeName)) { + return false; + } + final String methodName = methodExpression.getReferenceName(); + final String unboxingMethod = + s_unboxingMethods.get(qualifierTypeName); + return unboxingMethod.equals(methodName); + } + + private static boolean isSameMethodCalledWithoutUnboxing( + @NotNull PsiCallExpression callExpression, + @NotNull PsiMethodCallExpression unboxingExpression) { + final PsiExpressionList argumentList = + callExpression.getArgumentList(); + if (argumentList == null) { + return false; + } + final PsiExpression[] expressions = argumentList.getExpressions(); + final PsiMethod originalMethod = + callExpression.resolveMethod(); + if (originalMethod == null) { + return false; + } + final String name = originalMethod.getName(); + final PsiClass containingClass = + originalMethod.getContainingClass(); + if (containingClass == null) { + return false; + } + final PsiType[] types = new PsiType[expressions.length]; + for (int i = 0; i < expressions.length; i++) { + final PsiExpression expression = expressions[i]; + final PsiType type = expression.getType(); + if (unboxingExpression.equals(expression)) { + if (!(type instanceof PsiPrimitiveType)) { + return false; + } + final PsiPrimitiveType primitiveType = + (PsiPrimitiveType)type; + types[i] = primitiveType.getBoxedType(unboxingExpression); + } + else { + types[i] = type; + } + } + final PsiMethod[] methods = + containingClass.findMethodsByName(name, true); + for (PsiMethod method : methods) { + if (!originalMethod.equals(method)) { + if (MethodCallUtils.isApplicable(method, + PsiSubstitutor.EMPTY, types)) { + return false; + } + } + } + return true; + } + + @Nullable + private static PsiExpression getContainingExpression( + @NotNull PsiElement expression) { + final PsiElement parent = expression.getParent(); + if (parent == null || !(parent instanceof PsiExpression) && + !(parent instanceof PsiExpressionList)) { + return null; + } + if (parent instanceof PsiParenthesizedExpression || + parent instanceof PsiExpressionList) { + return getContainingExpression(parent); + } + else { + return (PsiExpression)parent; + } + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java new file mode 100644 index 000000000000..50ade6b36a09 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java @@ -0,0 +1,337 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.migration; + +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TypeUtils; +import com.siyeh.ig.psiutils.VariableAccessUtils; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WhileCanBeForeachInspectionBase extends BaseInspection { + @Nullable + public static PsiStatement getPreviousStatement(PsiElement context) { + final PsiElement prevStatement = PsiTreeUtil.skipSiblingsBackward(context, PsiWhiteSpace.class, PsiComment.class); + if (!(prevStatement instanceof PsiStatement)) { + return null; + } + return (PsiStatement)prevStatement; + } + + @Override + @NotNull + public String getID() { + return "WhileLoopReplaceableByForEach"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("while.can.be.foreach.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("while.can.be.foreach.problem.descriptor"); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new WhileCanBeForeachVisitor(); + } + + private static class WhileCanBeForeachVisitor extends BaseInspectionVisitor { + + @Override + public void visitWhileStatement(@NotNull PsiWhileStatement whileStatement) { + super.visitWhileStatement(whileStatement); + if (!PsiUtil.isLanguageLevel5OrHigher(whileStatement)) { + return; + } + if (!isCollectionLoopStatement(whileStatement)) { + return; + } + registerStatementError(whileStatement); + } + + private static boolean isCollectionLoopStatement(PsiWhileStatement whileStatement) { + final PsiStatement initialization = getPreviousStatement(whileStatement); + if (!(initialization instanceof PsiDeclarationStatement)) { + return false; + } + final PsiDeclarationStatement declaration = (PsiDeclarationStatement)initialization; + final PsiElement[] declaredElements = declaration.getDeclaredElements(); + if (declaredElements.length != 1) { + return false; + } + final PsiElement declaredElement = declaredElements[0]; + if (!(declaredElement instanceof PsiVariable)) { + return false; + } + final PsiVariable variable = (PsiVariable)declaredElement; + if (!TypeUtils.variableHasTypeOrSubtype(variable, CommonClassNames.JAVA_UTIL_ITERATOR, "java.util.ListIterator")) { + return false; + } + final PsiExpression initialValue = variable.getInitializer(); + if (initialValue == null) { + return false; + } + if (!(initialValue instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression initialCall = (PsiMethodCallExpression)initialValue; + final PsiExpressionList argumentList = initialCall.getArgumentList(); + final PsiExpression[] argument = argumentList.getExpressions(); + if (argument.length != 0) { + return false; + } + final PsiReferenceExpression initialMethodExpression = initialCall.getMethodExpression(); + @NonNls final String initialCallName = initialMethodExpression.getReferenceName(); + if (!"iterator".equals(initialCallName) && !"listIterator".equals(initialCallName)) { + return false; + } + final PsiExpression qualifier = initialMethodExpression.getQualifierExpression(); + if (qualifier instanceof PsiSuperExpression) { + return false; + } + final PsiClass qualifierClass; + if (qualifier != null) { + final PsiType qualifierType = qualifier.getType(); + if (!(qualifierType instanceof PsiClassType)) { + return false; + } + qualifierClass = ((PsiClassType)qualifierType).resolve(); + } + else { + qualifierClass = PsiTreeUtil.getParentOfType(whileStatement, PsiClass.class); + } + if (qualifierClass == null) { + return false; + } + if (!InheritanceUtil.isInheritor(qualifierClass, CommonClassNames.JAVA_LANG_ITERABLE)) { + return false; + } + final PsiExpression condition = whileStatement.getCondition(); + if (!isHasNextCalled(variable, condition)) { + return false; + } + final PsiStatement body = whileStatement.getBody(); + if (body == null) { + return false; + } + if (calculateCallsToIteratorNext(variable, body) != 1) { + return false; + } + if (isIteratorRemoveCalled(variable, body)) { + return false; + } + //noinspection SimplifiableIfStatement + if (isIteratorHasNextCalled(variable, body)) { + return false; + } + if (VariableAccessUtils.variableIsAssigned(variable, body)) { + return false; + } + if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, body)) { + return false; + } + PsiElement nextSibling = whileStatement.getNextSibling(); + while (nextSibling != null) { + if (VariableAccessUtils.variableValueIsUsed(variable, nextSibling)) { + return false; + } + nextSibling = nextSibling.getNextSibling(); + } + return true; + } + + private static boolean isHasNextCalled(PsiVariable iterator, PsiExpression condition) { + if (!(condition instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression call = (PsiMethodCallExpression)condition; + final PsiExpressionList argumentList = call.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 0) { + return false; + } + final PsiReferenceExpression methodExpression = call.getMethodExpression(); + @NonNls final String methodName = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.HAS_NEXT.equals(methodName)) { + return false; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (qualifier == null) { + return true; + } + if (!(qualifier instanceof PsiReferenceExpression)) { + return false; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + return iterator.equals(target); + } + + private static int calculateCallsToIteratorNext(PsiVariable iterator, PsiElement context) { + final NumberCallsToIteratorNextVisitor visitor = new NumberCallsToIteratorNextVisitor(iterator); + context.accept(visitor); + return visitor.getNumCallsToIteratorNext(); + } + + private static boolean isIteratorRemoveCalled(PsiVariable iterator, PsiElement context) { + final IteratorMethodCallVisitor visitor = new IteratorMethodCallVisitor(iterator); + context.accept(visitor); + return visitor.isMethodCalled(); + } + + private static boolean isIteratorHasNextCalled(PsiVariable iterator, PsiElement context) { + final IteratorHasNextVisitor visitor = new IteratorHasNextVisitor(iterator); + context.accept(visitor); + return visitor.isHasNextCalled(); + } + } + + private static class NumberCallsToIteratorNextVisitor extends JavaRecursiveElementVisitor { + + private int numCallsToIteratorNext = 0; + private final PsiVariable iterator; + + private NumberCallsToIteratorNextVisitor(PsiVariable iterator) { + this.iterator = iterator; + } + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression callExpression) { + super.visitMethodCallExpression(callExpression); + final PsiReferenceExpression methodExpression = callExpression.getMethodExpression(); + @NonNls final String methodName = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.NEXT.equals(methodName)) { + return; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (!(qualifier instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + if (!iterator.equals(target)) { + return; + } + numCallsToIteratorNext++; + } + + public int getNumCallsToIteratorNext() { + return numCallsToIteratorNext; + } + } + + private static class IteratorMethodCallVisitor extends JavaRecursiveElementVisitor { + + private boolean methodCalled = false; + private final PsiVariable iterator; + + IteratorMethodCallVisitor(PsiVariable iterator) { + this.iterator = iterator; + } + + @Override + public void visitElement(@NotNull PsiElement element) { + if (!methodCalled) { + super.visitElement(element); + } + } + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { + if (methodCalled) { + return; + } + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final String name = methodExpression.getReferenceName(); + if (HardcodedMethodConstants.NEXT.equals(name)) { + return; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (!(qualifier instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + if (iterator.equals(target)) { + methodCalled = true; + } + } + + public boolean isMethodCalled() { + return methodCalled; + } + } + + private static class IteratorHasNextVisitor extends JavaRecursiveElementVisitor { + + private boolean hasNextCalled = false; + private final PsiVariable iterator; + + private IteratorHasNextVisitor(PsiVariable iterator) { + this.iterator = iterator; + } + + @Override + public void visitElement(@NotNull PsiElement element) { + if (!hasNextCalled) { + super.visitElement(element); + } + } + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + @NonNls final String name = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.HAS_NEXT.equals(name)) { + return; + } + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (!(qualifier instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + if (iterator.equals(target)) { + hasNextCalled = true; + } + } + + public boolean isHasNextCalled() { + return hasNextCalled; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/AnnotationNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/AnnotationNamingConventionInspectionBase.java new file mode 100644 index 000000000000..2df5cac1c55d --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/AnnotationNamingConventionInspectionBase.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class AnnotationNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 8; + private static final int DEFAULT_MAX_LENGTH = 64; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "annotation.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String annotationName = (String)infos[0]; + if (annotationName.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "annotation.naming.convention.problem.descriptor.short"); + } + else if (annotationName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "annotation.naming.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message( + "annotation.naming.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[A-Z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (!aClass.isAnnotationType()) { + return; + } + final String name = aClass.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerClassError(aClass, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionBase.java new file mode 100644 index 000000000000..d0ce6de63884 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspectionBase.java @@ -0,0 +1,123 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.CommonClassNames; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiType; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.LibraryUtil; +import com.siyeh.ig.psiutils.MethodUtils; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class BooleanMethodNameMustStartWithQuestionInspectionBase extends BaseInspection { + @SuppressWarnings({"PublicField"}) + public boolean ignoreBooleanMethods = false; + @SuppressWarnings({"PublicField"}) + public boolean ignoreInAnnotationInterface = true; + @SuppressWarnings({"PublicField"}) + public boolean onlyWarnOnBaseMethods = true; + /** + * @noinspection PublicField + */ + @NonNls public String questionString = "is,can,has,should,could,will,shall,check,contains,equals,add,put,remove,startsWith,endsWith";List + questionList = new ArrayList(32); + + public BooleanMethodNameMustStartWithQuestionInspectionBase() { + parseString(questionString, questionList); + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("boolean.method.name.must.start.with.question.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("boolean.method.name.must.start.with.question.problem.descriptor"); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(questionString, questionList); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + questionString = formatString(questionList); + super.writeSettings(element); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BooleanMethodNameMustStartWithQuestionVisitor(); + } + + private class BooleanMethodNameMustStartWithQuestionVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + final PsiType returnType = method.getReturnType(); + if (returnType == null) { + return; + } + else if (!returnType.equals(PsiType.BOOLEAN)) { + if (ignoreBooleanMethods || !returnType.equalsToText(CommonClassNames.JAVA_LANG_BOOLEAN)) { + return; + } + } + if (ignoreInAnnotationInterface) { + final PsiClass containingClass = method.getContainingClass(); + if (containingClass != null && containingClass.isAnnotationType()) { + return; + } + } + final String name = method.getName(); + for (String question : questionList) { + if (name.startsWith(question)) { + return; + } + } + if (onlyWarnOnBaseMethods) { + if (MethodUtils.hasSuper(method)) { + return; + } + } + else if (LibraryUtil.isOverrideOfLibraryMethod(method)) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNamePrefixedWithPackageNameInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNamePrefixedWithPackageNameInspectionBase.java new file mode 100644 index 000000000000..f381faaa74d5 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNamePrefixedWithPackageNameInspectionBase.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ClassUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.StringTokenizer; + +public class ClassNamePrefixedWithPackageNameInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "class.name.prefixed.with.package.name.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "class.name.prefixed.with.package.name.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ClassNameBePrefixedWithPackageNameVisitor(); + } + + private static class ClassNameBePrefixedWithPackageNameVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so it doesn't drill down into inner classes + final String className = aClass.getName(); + if (className == null) { + return; + } + final PsiClass outerClass = + ClassUtils.getOutermostContainingClass(aClass); + final String qualifiedName = outerClass.getQualifiedName(); + if (qualifiedName == null) { + return; + } + if (className.equals(qualifiedName)) { + return; + } + final StringTokenizer tokenizer = + new StringTokenizer(qualifiedName, "."); + String currentPackageName = null; + String lastPackageName = null; + while (tokenizer.hasMoreTokens()) { + lastPackageName = currentPackageName; + currentPackageName = tokenizer.nextToken(); + } + if (lastPackageName == null) { + return; + } + final String lowercaseClassName = className.toLowerCase(); + final String lowercasePackageName = lastPackageName.toLowerCase(); + if (!lowercaseClassName.startsWith(lowercasePackageName)) { + return; + } + registerClassError(aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNameSameAsAncestorNameInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNameSameAsAncestorNameInspectionBase.java new file mode 100644 index 000000000000..b54ae74f9799 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNameSameAsAncestorNameInspectionBase.java @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +public class ClassNameSameAsAncestorNameInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "class.name.same.as.ancestor.name.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "class.name.same.as.ancestor.name.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ClassNameSameAsAncestorNameVisitor(); + } + + private static class ClassNameSameAsAncestorNameVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so it doesn't drill down into inner classes + final String className = aClass.getName(); + if (className == null) { + return; + } + final Set alreadyVisited = new HashSet(8); + final PsiClass[] supers = aClass.getSupers(); + for (final PsiClass aSuper : supers) { + if (hasMatchingName(aSuper, className, alreadyVisited)) { + registerClassError(aClass); + } + } + } + + private static boolean hasMatchingName(PsiClass aSuper, + String className, + Set alreadyVisited) { + if (aSuper == null) { + return false; + } + if (alreadyVisited.contains(aSuper)) { + return false; + } + alreadyVisited.add(aSuper); + final String superName = aSuper.getName(); + if (className.equals(superName)) { + return true; + } + final PsiClass[] supers = aSuper.getSupers(); + for (PsiClass aSupers : supers) { + if (hasMatchingName(aSupers, className, alreadyVisited)) { + return true; + } + } + return false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNamingConventionInspectionBase.java new file mode 100644 index 000000000000..ede181d76220 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ClassNamingConventionInspectionBase.java @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiTypeParameter; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class ClassNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 8; + private static final int DEFAULT_MAX_LENGTH = 64; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "class.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String className = (String)infos[0]; + if (className.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "class.name.convention.problem.descriptor.short"); + } + else if (className.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "class.name.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message( + "class.name.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[A-Z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (aClass.isInterface() || aClass.isAnnotationType() || + aClass.isEnum()) { + return; + } + if (aClass instanceof PsiTypeParameter) { + return; + } + final String name = aClass.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerClassError(aClass, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConfusingMainMethodInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConfusingMainMethodInspectionBase.java new file mode 100644 index 000000000000..5cddcb5615ae --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConfusingMainMethodInspectionBase.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.*; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TypeUtils; +import org.jetbrains.annotations.NotNull; + +public class ConfusingMainMethodInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "confusing.main.method.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "confusing.main.method.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ConfusingMainMethodVisitor(); + } + + private static class ConfusingMainMethodVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod aMethod) { + // no call to super, so it doesn't drill down into inner classes + final String methodName = aMethod.getName(); + if (!HardcodedMethodConstants.MAIN.equals(methodName)) { + return; + } + if (!aMethod.hasModifierProperty(PsiModifier.PUBLIC)) { + registerMethodError(aMethod); + return; + } + if (!aMethod.hasModifierProperty(PsiModifier.STATIC)) { + registerMethodError(aMethod); + return; + } + final PsiType returnType = aMethod.getReturnType(); + + if (!TypeUtils.typeEquals(PsiKeyword.VOID, returnType)) { + registerMethodError(aMethod); + return; + } + final PsiParameterList parameterList = aMethod.getParameterList(); + if (parameterList.getParametersCount() != 1) { + registerMethodError(aMethod); + return; + } + final PsiParameter[] parameters = parameterList.getParameters(); + final PsiType parameterType = parameters[0].getType(); + if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING + "[]", + parameterType)) { + registerMethodError(aMethod); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConstantNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConstantNamingConventionInspectionBase.java new file mode 100644 index 000000000000..3ae903515ab6 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConstantNamingConventionInspectionBase.java @@ -0,0 +1,103 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiEnumConstant; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiType; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ClassUtils; +import org.jetbrains.annotations.NotNull; + +public class ConstantNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 5; + private static final int DEFAULT_MAX_LENGTH = 32; + @SuppressWarnings({"PublicField"}) + public boolean onlyCheckImmutables = false; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "constant.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String fieldName = (String)infos[0]; + if (fieldName.length() < getMinLength()) { + return InspectionGadgetsBundle.message("constant.naming.convention.problem.descriptor.short"); + } + else if (fieldName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message("constant.naming.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message("constant.naming.convention.problem.descriptor.regex.mismatch", getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[A-Z][A-Z_\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitField(@NotNull PsiField field) { + super.visitField(field); + if (field instanceof PsiEnumConstant) { + return; + } + if (!field.hasModifierProperty(PsiModifier.STATIC) || !field.hasModifierProperty(PsiModifier.FINAL)) { + return; + } + final String name = field.getName(); + if (name == null) { + return; + } + final PsiType type = field.getType(); + if (onlyCheckImmutables && !ClassUtils.isImmutable(type)) { + return; + } + if (isValid(name)) { + return; + } + registerFieldError(field, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConventionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConventionInspection.java new file mode 100644 index 000000000000..7afacaf647fd --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ConventionInspection.java @@ -0,0 +1,96 @@ +/* + * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.codeInspection.ui.ConventionOptionsPanel; +import com.intellij.openapi.util.InvalidDataException; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.ig.BaseInspection; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class ConventionInspection extends BaseInspection { + + public static final JComponent[] EMPTY_JCOMPONENT_ARRAY = {}; + /** + * @noinspection PublicField + */ + public String m_regex = getDefaultRegex(); + /** + * @noinspection PublicField + */ + public int m_minLength = getDefaultMinLength(); + /** + * @noinspection PublicField + */ + public int m_maxLength = getDefaultMaxLength(); + + protected Pattern m_regexPattern = Pattern.compile(m_regex); + + @NonNls + protected abstract String getDefaultRegex(); + + protected abstract int getDefaultMinLength(); + + protected abstract int getDefaultMaxLength(); + + protected String getRegex() { + return m_regex; + } + + protected int getMinLength() { + return m_minLength; + } + + protected int getMaxLength() { + return m_maxLength; + } + + protected boolean isValid(String name) { + final int length = name.length(); + if (length < m_minLength) { + return false; + } + if (m_maxLength > 0 && length > m_maxLength) { + return false; + } + if (HardcodedMethodConstants.SERIAL_VERSION_UID.equals(name)) { + return true; + } + final Matcher matcher = m_regexPattern.matcher(name); + return matcher.matches(); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + m_regexPattern = Pattern.compile(m_regex); + } + + public JComponent[] createExtraOptions() { + return EMPTY_JCOMPONENT_ARRAY; + } + + @Override + public final JComponent createOptionsPanel() { + return new ConventionOptionsPanel(this, "m_minLength", "m_maxLength", "m_regex", "m_regexPattern", createExtraOptions()); + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/DollarSignInNameInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/DollarSignInNameInspectionBase.java new file mode 100644 index 000000000000..7287dc5fa21b --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/DollarSignInNameInspectionBase.java @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiVariable; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class DollarSignInNameInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "dollar.sign.in.name.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "dollar.sign.in.name.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new DollarSignInNameVisitor(); + } + + private static class DollarSignInNameVisitor extends BaseInspectionVisitor { + + @Override + public void visitVariable(@NotNull PsiVariable variable) { + super.visitVariable(variable); + final String name = variable.getName(); + if (name == null) { + return; + } + if (name.indexOf((int)'$') < 0) { + return; + } + registerVariableError(variable); + } + + @Override + public void visitMethod(@NotNull PsiMethod method) { + super.visitMethod(method); + final String name = method.getName(); + if (name.indexOf((int)'$') < 0) { + return; + } + registerMethodError(method); + } + + @Override + public void visitClass(@NotNull PsiClass aClass) { + //note: no call to super, to avoid drill-down + final String name = aClass.getName(); + if (name == null) { + return; + } + if (name.indexOf((int)'$') < 0) { + return; + } + registerClassError(aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/EnumeratedClassNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/EnumeratedClassNamingConventionInspectionBase.java new file mode 100644 index 000000000000..09cb8e6b338f --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/EnumeratedClassNamingConventionInspectionBase.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class EnumeratedClassNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 8; + private static final int DEFAULT_MAX_LENGTH = 64; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "enumerated.class.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String className = (String)infos[0]; + if (className.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "enumerated.class.naming.convention.problem.descriptor.short"); + } + else if (className.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "enumerated.class.naming.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message( + "enumerated.class.naming.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[A-Z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (!aClass.isEnum()) { + return; + } + final String name = aClass.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerClassError(aClass, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/EnumeratedConstantNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/EnumeratedConstantNamingConventionInspectionBase.java new file mode 100644 index 000000000000..5cb0ba9fc3dd --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/EnumeratedConstantNamingConventionInspectionBase.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiEnumConstant; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class EnumeratedConstantNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 5; + private static final int DEFAULT_MAX_LENGTH = 32; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("enumerated.constant.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String fieldName = (String)infos[0]; + if (fieldName.length() < getMinLength()) { + return InspectionGadgetsBundle.message("enumerated.constant.naming.convention.problem.descriptor.short"); + } + else if (fieldName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message("enumerated.constant.naming.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message("enumerated.constant.naming.convention.problem.descriptor.regex.mismatch", getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[A-Z][A-Z_\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitEnumConstant(PsiEnumConstant constant) { + super.visitEnumConstant(constant); + final String name = constant.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerFieldError(constant, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ExceptionNameDoesntEndWithExceptionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ExceptionNameDoesntEndWithExceptionInspectionBase.java new file mode 100644 index 000000000000..f4cf0a4e57b0 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ExceptionNameDoesntEndWithExceptionInspectionBase.java @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.CommonClassNames; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiTypeParameter; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class ExceptionNameDoesntEndWithExceptionInspectionBase extends BaseInspection { + @Override + @NotNull + public String getID() { + return "ExceptionClassNameDoesntEndWithException"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "exception.name.doesnt.end.with.exception.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "exception.name.doesnt.end.with.exception.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ExceptionNameDoesntEndWithExceptionVisitor(); + } + + private static class ExceptionNameDoesntEndWithExceptionVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so it doesn't drill down into inner classes + if (aClass instanceof PsiTypeParameter) { + return; + } + final String className = aClass.getName(); + if (className == null) { + return; + } + @NonNls final String exception = "Exception"; + if (className.endsWith(exception)) { + return; + } + if (!InheritanceUtil.isInheritor(aClass, + CommonClassNames.JAVA_LANG_EXCEPTION)) { + return; + } + registerClassError(aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InstanceMethodNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InstanceMethodNamingConventionInspectionBase.java new file mode 100644 index 000000000000..9f1e86052986 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InstanceMethodNamingConventionInspectionBase.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiIdentifier; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiModifier; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.LibraryUtil; +import com.siyeh.ig.psiutils.MethodUtils; +import org.jetbrains.annotations.NotNull; + +public class InstanceMethodNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 4; + private static final int DEFAULT_MAX_LENGTH = 32; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("instance.method.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String methodName = (String)infos[0]; + if (methodName.length() < getMinLength()) { + return InspectionGadgetsBundle.message("instance.method.name.convention.problem.descriptor.short"); + } + else if (methodName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message("instance.method.name.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message("instance.method.name.convention.problem.descriptor.regex.mismatch", getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[a-z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + super.visitMethod(method); + if (method.isConstructor() || method.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + final PsiIdentifier nameIdentifier = method.getNameIdentifier(); + if (nameIdentifier == null) { + return; + } + final String name = method.getName(); + if (isValid(name)) { + return; + } + if (!isOnTheFly()) { + if (MethodUtils.hasSuper(method)) { + return; + } + } + if (LibraryUtil.isOverrideOfLibraryMethod(method)) { + return; + } + registerMethodError(method, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InstanceVariableNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InstanceVariableNamingConventionInspectionBase.java new file mode 100644 index 000000000000..53cac98431a3 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InstanceVariableNamingConventionInspectionBase.java @@ -0,0 +1,95 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiModifier; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class InstanceVariableNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 5; + private static final int DEFAULT_MAX_LENGTH = 32; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "instance.variable.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String fieldName = (String)infos[0]; + if (fieldName.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "instance.variable.name.convention.problem.descriptor.short"); + } + else if (fieldName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "instance.variable.name.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message( + "instance.variable.name.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "m_[a-z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitField(@NotNull PsiField field) { + super.visitField(field); + if (field.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + final String name = field.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerFieldError(field, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InterfaceNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InterfaceNamingConventionInspectionBase.java new file mode 100644 index 000000000000..9a1049fc3f5e --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/InterfaceNamingConventionInspectionBase.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class InterfaceNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 8; + private static final int DEFAULT_MAX_LENGTH = 64; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "interface.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String interfaceName = (String)infos[0]; + if (interfaceName.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "interface.name.convention.problem.descriptor.short"); + } + else if (interfaceName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "interface.name.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message( + "interface.name.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[A-Z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (!aClass.isInterface() || aClass.isAnnotationType()) { + return; + } + final String name = aClass.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerClassError(aClass, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/LocalVariableNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/LocalVariableNamingConventionInspectionBase.java new file mode 100644 index 000000000000..5f4f13ee4c3c --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/LocalVariableNamingConventionInspectionBase.java @@ -0,0 +1,133 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class LocalVariableNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 1; + private static final int DEFAULT_MAX_LENGTH = 20; + /** + * @noinspection PublicField + */ + public boolean m_ignoreForLoopParameters = false; + /** + * @noinspection PublicField + */ + public boolean m_ignoreCatchParameters = false; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("local.variable.naming.convention.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String varName = (String)infos[0]; + if (varName.length() < getMinLength()) { + return InspectionGadgetsBundle.message("local.variable.naming.convention.problem.descriptor.short"); + } + else if (varName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message("local.variable.naming.convention.problem.descriptor.long"); + } + else { + return InspectionGadgetsBundle.message("local.variable.naming.convention.problem.descriptor.regex.mismatch", getRegex()); + } + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + protected String getDefaultRegex() { + return "[a-z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitLocalVariable(@NotNull PsiLocalVariable variable) { + super.visitLocalVariable(variable); + if (m_ignoreForLoopParameters) { + final PsiElement parent = variable.getParent(); + if (parent != null) { + final PsiElement grandparent = parent.getParent(); + if (grandparent instanceof PsiForStatement) { + final PsiForStatement forLoop = (PsiForStatement)grandparent; + final PsiStatement initialization = forLoop.getInitialization(); + if (parent.equals(initialization)) { + return; + } + } + } + } + final String name = variable.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerVariableError(variable, name); + } + + @Override + public void visitParameter(@NotNull PsiParameter variable) { + final PsiElement scope = variable.getDeclarationScope(); + final boolean isCatchParameter = scope instanceof PsiCatchSection; + final boolean isForeachParameter = scope instanceof PsiForeachStatement; + if (!isCatchParameter && !isForeachParameter) { + return; + } + if (m_ignoreCatchParameters && isCatchParameter) { + return; + } + if (m_ignoreForLoopParameters && isForeachParameter) { + return; + } + final String name = variable.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerVariableError(variable, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNameSameAsClassNameInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNameSameAsClassNameInspectionBase.java new file mode 100644 index 000000000000..e5bcd718a1ad --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNameSameAsClassNameInspectionBase.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class MethodNameSameAsClassNameInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "method.name.same.as.class.name.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "method.name.same.as.class.name.problem.descriptor"); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MethodNameSameAsClassNameVisitor(); + } + + private static class MethodNameSameAsClassNameVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + // no call to super, so it doesn't drill down into inner classes + if (method.isConstructor()) { + return; + } + final String methodName = method.getName(); + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) { + return; + } + final String className = containingClass.getName(); + if (className == null) { + return; + } + if (!methodName.equals(className)) { + return; + } + registerMethodError(method, Boolean.valueOf(isOnTheFly())); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNameSameAsParentNameInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNameSameAsParentNameInspectionBase.java new file mode 100644 index 000000000000..7ac137c5444e --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNameSameAsParentNameInspectionBase.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class MethodNameSameAsParentNameInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "method.name.same.as.parent.name.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "method.name.same.as.parent.name.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MethodNameSameAsParentClassNameVisitor(); + } + + private static class MethodNameSameAsParentClassNameVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + // no call to super, so it doesn't drill down into inner classes + if (method.isConstructor()) { + return; + } + if (method.getNameIdentifier() == null) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) { + return; + } + final PsiClass parent = containingClass.getSuperClass(); + if (parent == null) { + return; + } + final String parentName = parent.getName(); + if (parentName == null) { + return; + } + final String methodName = method.getName(); + if (!methodName.equals(parentName)) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNamesDifferOnlyByCaseInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNamesDifferOnlyByCaseInspectionBase.java new file mode 100644 index 000000000000..4fa3b520bc2e --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/MethodNamesDifferOnlyByCaseInspectionBase.java @@ -0,0 +1,87 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiIdentifier; +import com.intellij.psi.PsiMethod; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.MethodUtils; +import org.jetbrains.annotations.NotNull; + +public class MethodNamesDifferOnlyByCaseInspectionBase extends BaseInspection { + @SuppressWarnings("PublicField") + public boolean ignoreIfMethodIsOverride = true; + + @Override + @NotNull + public String getID() { + return "MethodNamesDifferingOnlyByCase"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("method.names.differ.only.by.case.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("method.names.differ.only.by.case.problem.descriptor", infos[0]); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MethodNamesDifferOnlyByCaseVisitor(); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + private class MethodNamesDifferOnlyByCaseVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + if (method.isConstructor()) { + return; + } + final PsiIdentifier nameIdentifier = method.getNameIdentifier(); + if (nameIdentifier == null) { + return; + } + final String methodName = method.getName(); + if (ignoreIfMethodIsOverride && MethodUtils.hasSuper(method)) { + return; + } + final PsiClass aClass = method.getContainingClass(); + if (aClass == null) { + return; + } + final PsiMethod[] methods = aClass.getAllMethods(); + for (PsiMethod testMethod : methods) { + final String testMethodName = testMethod.getName(); + if (!methodName.equals(testMethodName) && methodName.equalsIgnoreCase(testMethodName)) { + registerError(nameIdentifier, testMethodName); + } + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspectionBase.java new file mode 100644 index 000000000000..80d9fbb7fc1f --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspectionBase.java @@ -0,0 +1,125 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.CommonClassNames; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiType; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.LibraryUtil; +import com.siyeh.ig.psiutils.MethodUtils; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class NonBooleanMethodNameMayNotStartWithQuestionInspectionBase extends BaseInspection { + public NonBooleanMethodNameMayNotStartWithQuestionInspectionBase() { + parseString(questionString, questionList); + } + + /** + * @noinspection PublicField + */ + @NonNls public String questionString = "is,can,has,should,could,will,shall,check,contains,equals,startsWith,endsWith"; + @SuppressWarnings({"PublicField"}) + public boolean ignoreBooleanMethods = false; + @SuppressWarnings({"PublicField"}) + public boolean onlyWarnOnBaseMethods = true;List questionList = new ArrayList(32); + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("non.boolean.method.name.must.not.start.with.question.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("non.boolean.method.name.must.not.start.with.question.problem.descriptor"); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(questionString, questionList); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + questionString = formatString(questionList); + super.writeSettings(element); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NonBooleanMethodNameMayNotStartWithQuestionVisitor(); + } + + private class NonBooleanMethodNameMayNotStartWithQuestionVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + super.visitMethod(method); + final PsiType returnType = method.getReturnType(); + if (returnType == null || returnType.equals(PsiType.BOOLEAN)) { + return; + } + if (ignoreBooleanMethods && returnType.equalsToText(CommonClassNames.JAVA_LANG_BOOLEAN)) { + return; + } + final String name = method.getName(); + boolean startsWithQuestionWord = false; + for (String question : questionList) { + if (name.startsWith(question)) { + if (name.length() == question.length()) { + startsWithQuestionWord = true; + break; + } + final char nextChar = name.charAt(question.length()); + if (Character.isUpperCase(nextChar) || nextChar == '_') { + startsWithQuestionWord = true; + break; + } + } + } + if (!startsWithQuestionWord) { + return; + } + if (onlyWarnOnBaseMethods) { + if (MethodUtils.hasSuper(method)) { + return; + } + } + else if (LibraryUtil.isOverrideOfLibraryMethod(method)) { + return; + } + registerMethodError(method); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonExceptionNameEndsWithExceptionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonExceptionNameEndsWithExceptionInspectionBase.java new file mode 100644 index 000000000000..7dfd0199605a --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/NonExceptionNameEndsWithExceptionInspectionBase.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.CommonClassNames; +import com.intellij.psi.PsiClass; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class NonExceptionNameEndsWithExceptionInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "non.exception.name.ends.with.exception.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "non.exception.name.ends.with.exception.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NonExceptionNameEndsWithExceptionVisitor(); + } + + private static class NonExceptionNameEndsWithExceptionVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so it doesn't drill down into inner classes + final String className = aClass.getName(); + if (className == null) { + return; + } + @NonNls final String exception = "Exception"; + if (!className.endsWith(exception)) { + return; + } + if (InheritanceUtil.isInheritor(aClass, + CommonClassNames.JAVA_LANG_EXCEPTION)) { + return; + } + registerClassError(aClass, className, + Boolean.valueOf(isOnTheFly())); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/OverloadedMethodsWithSameNumberOfParametersInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/OverloadedMethodsWithSameNumberOfParametersInspection.java new file mode 100644 index 000000000000..b3efd217b8a4 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/OverloadedMethodsWithSameNumberOfParametersInspection.java @@ -0,0 +1,108 @@ +/* + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.MethodUtils; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class OverloadedMethodsWithSameNumberOfParametersInspection extends BaseInspection { + + @SuppressWarnings({"PublicField"}) + public boolean ignoreInconvertibleTypes = true; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("overloaded.methods.with.same.number.parameters.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("overloaded.methods.with.same.number.parameters.problem.descriptor"); + } + + @Override + public JComponent createOptionsPanel() { + return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message( + "overloaded.methods.with.same.number.parameters.option"), this, "ignoreInconvertibleTypes"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new OverloadedMethodsWithSameNumberOfParametersVisitor(); + } + + private class OverloadedMethodsWithSameNumberOfParametersVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + if (method.isConstructor()) { + return; + } + if (method.getNameIdentifier() == null) { + return; + } + final PsiParameterList parameterList = method.getParameterList(); + final int parameterCount = parameterList.getParametersCount(); + if (parameterCount == 0) { + return; + } + final PsiClass aClass = method.getContainingClass(); + if (aClass == null) { + return; + } + if (MethodUtils.hasSuper(method)) { + return; + } + final String methodName = method.getName(); + final PsiMethod[] sameNameMethods = aClass.findMethodsByName(methodName, false); + for (PsiMethod sameNameMethod : sameNameMethods) { + if (method.equals(sameNameMethod)) { + continue; + } + final PsiParameterList otherParameterList = sameNameMethod.getParameterList(); + if (parameterCount == otherParameterList.getParametersCount()) { + if (ignoreInconvertibleTypes && !areParameterTypesConvertible(parameterList, otherParameterList)) { + return; + } + registerMethodError(method); + return; + } + } + } + + private boolean areParameterTypesConvertible(PsiParameterList parameterList, PsiParameterList otherParameterList) { + final PsiParameter[] parameters = parameterList.getParameters(); + final PsiParameter[] otherParameters = otherParameterList.getParameters(); + for (int i = 0; i < parameters.length; i++) { + final PsiType type = parameters[i].getType(); + final PsiType otherType = otherParameters[i].getType(); + if (!type.isAssignableFrom(otherType) && !otherType.isAssignableFrom(type)) { + return false; + } + } + return true; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/OverloadedVarargsMethodInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/OverloadedVarargsMethodInspection.java new file mode 100644 index 000000000000..8be3f7e20d3f --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/OverloadedVarargsMethodInspection.java @@ -0,0 +1,75 @@ +/* + * Copyright 2006-2007 Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class OverloadedVarargsMethodInspection extends BaseInspection { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "overloaded.vararg.method.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final PsiMethod element = (PsiMethod)infos[0]; + if (element.isConstructor()) { + return InspectionGadgetsBundle.message( + "overloaded.vararg.constructor.problem.descriptor"); + } + else { + return InspectionGadgetsBundle.message( + "overloaded.vararg.method.problem.descriptor"); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new OverloadedVarargMethodVisitor(); + } + + private static class OverloadedVarargMethodVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + if (!method.isVarArgs()) { + return; + } + final PsiClass aClass = method.getContainingClass(); + if (aClass == null) { + return; + } + final String methodName = method.getName(); + final PsiMethod[] sameNameMethods = + aClass.findMethodsByName(methodName, false); + for (PsiMethod sameNameMethod : sameNameMethods) { + if (!sameNameMethod.equals(method)) { + registerMethodError(method, method); + } + } + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/PackageNamingConventionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/PackageNamingConventionInspection.java new file mode 100644 index 000000000000..7bad29330828 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/PackageNamingConventionInspection.java @@ -0,0 +1,186 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.analysis.AnalysisScope; +import com.intellij.codeInspection.CommonProblemDescriptor; +import com.intellij.codeInspection.GlobalInspectionContext; +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.reference.RefEntity; +import com.intellij.codeInspection.reference.RefPackage; +import com.intellij.codeInspection.ui.ConventionOptionsPanel; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.psi.PsiJavaCodeReferenceElement; +import com.intellij.psi.PsiPackageStatement; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseGlobalInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.BaseSharedLocalInspection; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PackageNamingConventionInspection extends BaseGlobalInspection { + + private static final int DEFAULT_MIN_LENGTH = 3; + private static final int DEFAULT_MAX_LENGTH = 16; + /** + * @noinspection PublicField + */ + @NonNls + public String m_regex = "[a-z]*"; + + /** + * @noinspection PublicField + */ + public int m_minLength = DEFAULT_MIN_LENGTH; + + /** + * @noinspection PublicField + */ + public int m_maxLength = DEFAULT_MAX_LENGTH; + + protected Pattern m_regexPattern = Pattern.compile(m_regex); + + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("package.naming.convention.display.name"); + } + + @Override + @Nullable + public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity, @NotNull AnalysisScope analysisScope, + @NotNull InspectionManager inspectionManager, + @NotNull GlobalInspectionContext globalInspectionContext) { + if (!(refEntity instanceof RefPackage)) { + return null; + } + @NonNls final String name = refEntity.getName(); + if ("default package".equals(name)) { + return null; + } + + final int length = name.length(); + if (length < m_minLength) { + final String errorString = InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.short", name); + return new CommonProblemDescriptor[]{inspectionManager.createProblemDescriptor(errorString)}; + } + if (length > m_maxLength) { + final String errorString = InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.long", name); + return new CommonProblemDescriptor[]{inspectionManager.createProblemDescriptor(errorString)}; + } + final Matcher matcher = m_regexPattern.matcher(name); + if (matcher.matches()) { + return null; + } + else { + final String errorString = + InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.regex.mismatch", name, m_regex); + return new CommonProblemDescriptor[]{inspectionManager.createProblemDescriptor(errorString)}; + } + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + m_regexPattern = Pattern.compile(m_regex); + } + + @Override + public JComponent createOptionsPanel() { + return new ConventionOptionsPanel(this, "m_minLength", "m_maxLength", "m_regex", "m_regexPattern"); + } + + boolean isValid(String name) { + final int length = name.length(); + if (length < m_minLength) { + return false; + } + if (m_maxLength > 0 && length > m_maxLength) { + return false; + } + if (HardcodedMethodConstants.SERIAL_VERSION_UID.equals(name)) { + return true; + } + final Matcher matcher = m_regexPattern.matcher(name); + return matcher.matches(); + } + + @Nullable + public LocalInspectionTool getSharedLocalInspectionTool() { + return new LocalPackageNamingConventionInspection(this); + } + + private static class LocalPackageNamingConventionInspection extends BaseSharedLocalInspection { + + public LocalPackageNamingConventionInspection(PackageNamingConventionInspection inspection) { + super(inspection); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + final String name = (String)infos[0]; + if (name.length() < mySettingsDelegate.m_minLength) { + return InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.short", name); + } + else if (name.length() > mySettingsDelegate.m_maxLength) { + return InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.long", name); + } + else { + return InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.regex.mismatch", + name, mySettingsDelegate.m_regex); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BaseInspectionVisitor() { + + @Override + public void visitPackageStatement(PsiPackageStatement statement) { + final PsiJavaCodeReferenceElement reference = statement.getPackageReference(); + if (reference == null) { + return; + } + final String text = reference.getText(); + int start = 0; + int index = text.indexOf('.', start); + while (index > 0) { + final String name = text.substring(start, index); + if (!mySettingsDelegate.isValid(name)) { + registerErrorAtOffset(reference, start, index - start, name); + } + start = index + 1; + index = text.indexOf('.', start); + } + final String lastName = text.substring(start); + if (!mySettingsDelegate.isValid(lastName)) { + registerErrorAtOffset(reference, start, lastName.length(), lastName); + } + } + }; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ParameterNameDiffersFromOverriddenParameterInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ParameterNameDiffersFromOverriddenParameterInspectionBase.java new file mode 100644 index 000000000000..6464e0efcd27 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ParameterNameDiffersFromOverriddenParameterInspectionBase.java @@ -0,0 +1,115 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameter; +import com.intellij.psi.PsiParameterList; +import com.intellij.psi.search.searches.SuperMethodsSearch; +import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; +import com.intellij.util.Query; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.LibraryUtil; +import org.jetbrains.annotations.NotNull; + +public class ParameterNameDiffersFromOverriddenParameterInspectionBase extends BaseInspection { + /** + * @noinspection PublicField + */ + public boolean m_ignoreSingleCharacterNames = false; + /** + * @noinspection PublicField + */ + public boolean m_ignoreOverridesOfLibraryMethods = false; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "parameter.name.differs.from.overridden.parameter.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "parameter.name.differs.from.overridden.parameter.problem.descriptor", + infos[0]); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ParameterNameDiffersFromOverriddenParameterVisitor(); + } + + private class ParameterNameDiffersFromOverriddenParameterVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() == 0) { + return; + } + final Query query = + SuperMethodsSearch.search( + method, method.getContainingClass(), true, false); + final MethodSignatureBackedByPsiMethod methodSignature = + query.findFirst(); + if (methodSignature == null) { + return; + } + final PsiMethod superMethod = methodSignature.getMethod(); + final PsiParameter[] parameters = parameterList.getParameters(); + checkParameters(superMethod, parameters); + } + + private void checkParameters(PsiMethod superMethod, + PsiParameter[] parameters) { + if (m_ignoreOverridesOfLibraryMethods) { + final PsiClass containingClass = + superMethod.getContainingClass(); + if (containingClass != null && + LibraryUtil.classIsInLibrary(containingClass)) { + return; + } + } + final PsiParameterList superParameterList = + superMethod.getParameterList(); + final PsiParameter[] superParameters = + superParameterList.getParameters(); + for (int i = 0; i < parameters.length; i++) { + final PsiParameter parameter = parameters[i]; + final String parameterName = parameter.getName(); + final String superParameterName = superParameters[i].getName(); + if (superParameterName == null) { + continue; + } + if (superParameterName.equals(parameterName)) { + continue; + } + if (m_ignoreSingleCharacterNames && + superParameterName.length() == 1) { + continue; + } + registerVariableError(parameter, superParameterName); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ParameterNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ParameterNamingConventionInspectionBase.java new file mode 100644 index 000000000000..a4f1e11071cf --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/ParameterNamingConventionInspectionBase.java @@ -0,0 +1,103 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiCatchSection; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiForeachStatement; +import com.intellij.psi.PsiParameter; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class ParameterNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 1; + private static final int DEFAULT_MAX_LENGTH = 20; + + @Override + @NotNull + public String getID() { + return "MethodParameterNamingConvention"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "parameter.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String parametername = (String)infos[0]; + if (parametername.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "parameter.naming.convention.problem.descriptor.short"); + } + else if (parametername.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "parameter.naming.convention.problem.descriptor.long"); + } + else { + return InspectionGadgetsBundle.message( + "parameter.naming.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + } + + @Override + protected String getDefaultRegex() { + return "[a-z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitParameter(@NotNull PsiParameter variable) { + final PsiElement scope = variable.getDeclarationScope(); + if (scope instanceof PsiCatchSection || + scope instanceof PsiForeachStatement) { + return; + } + final String name = variable.getName(); + if (name == null || isValid(name)) { + return; + } + registerVariableError(variable, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/QuestionableNameInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/QuestionableNameInspectionBase.java new file mode 100644 index 000000000000..0fae4532753a --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/QuestionableNameInspectionBase.java @@ -0,0 +1,113 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiVariable; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class QuestionableNameInspectionBase extends BaseInspection { + public QuestionableNameInspectionBase() { + parseString(nameString, nameList); + } + + /** + * @noinspection PublicField + */ + @NonNls public String nameString = "aa,abc,bad,bar,bar2,baz,baz1,baz2," + + "baz3,bb,blah,bogus,bool,cc,dd,defau1t,dummy,dummy2,ee,fa1se," + + "ff,foo,foo1,foo2,foo3,foobar,four,fred,fred1,fred2,gg,hh,hello," + + "hello1,hello2,hello3,ii,nu11,one,silly,silly2,string,two,that," + + "then,three,whi1e,var";List nameList = new ArrayList(32); + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "questionable.name.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "questionable.name.problem.descriptor"); + } + + @Override + public void readSettings(@NotNull Element element) throws InvalidDataException { + super.readSettings(element); + parseString(nameString, nameList); + } + + @Override + public void writeSettings(@NotNull Element element) throws WriteExternalException { + nameString = formatString(nameList); + super.writeSettings(element); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new QuestionableNameVisitor(); + } + + private class QuestionableNameVisitor extends BaseInspectionVisitor { + + private final Set nameSet = new HashSet(nameList); + + @Override + public void visitVariable(@NotNull PsiVariable variable) { + final String name = variable.getName(); + if (nameSet.contains(name)) { + registerVariableError(variable); + } + } + + @Override + public void visitMethod(@NotNull PsiMethod method) { + final String name = method.getName(); + if (nameSet.contains(name)) { + registerMethodError(method); + } + } + + @Override + public void visitClass(@NotNull PsiClass aClass) { + final String name = aClass.getName(); + if (nameSet.contains(name)) { + registerClassError(aClass); + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StandardVariableNamesInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StandardVariableNamesInspectionBase.java new file mode 100644 index 000000000000..d2dd8c60b160 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StandardVariableNamesInspectionBase.java @@ -0,0 +1,155 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public class StandardVariableNamesInspectionBase extends BaseInspection { + @NonNls static final Map s_expectedTypes = + new HashMap(13); + @NonNls static final Map s_boxingClasses = + new HashMap(8); + @SuppressWarnings("PublicField") + public boolean ignoreParameterNameSameAsSuper = false; + static { + s_expectedTypes.put("b", "byte"); + s_expectedTypes.put("c", "char"); + s_expectedTypes.put("ch", "char"); + s_expectedTypes.put("d", "double"); + s_expectedTypes.put("f", "float"); + s_expectedTypes.put("i", "int"); + s_expectedTypes.put("j", "int"); + s_expectedTypes.put("k", "int"); + s_expectedTypes.put("m", "int"); + s_expectedTypes.put("n", "int"); + s_expectedTypes.put("l", "long"); + s_expectedTypes.put("s", CommonClassNames.JAVA_LANG_STRING); + s_expectedTypes.put("str", CommonClassNames.JAVA_LANG_STRING); + + s_boxingClasses.put("int", CommonClassNames.JAVA_LANG_INTEGER); + s_boxingClasses.put("short", CommonClassNames.JAVA_LANG_SHORT); + s_boxingClasses.put("boolean", CommonClassNames.JAVA_LANG_BOOLEAN); + s_boxingClasses.put("long", CommonClassNames.JAVA_LANG_LONG); + s_boxingClasses.put("byte", CommonClassNames.JAVA_LANG_BYTE); + s_boxingClasses.put("float", CommonClassNames.JAVA_LANG_FLOAT); + s_boxingClasses.put("double", CommonClassNames.JAVA_LANG_DOUBLE); + s_boxingClasses.put("char", CommonClassNames.JAVA_LANG_CHARACTER); + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "standard.variable.names.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final PsiVariable variable = (PsiVariable)infos[0]; + final String name = variable.getName(); + final String expectedType = s_expectedTypes.get(name); + if (PsiUtil.isLanguageLevel5OrHigher(variable)) { + final String boxedType = s_boxingClasses.get(expectedType); + if (boxedType != null) { + return InspectionGadgetsBundle.message( + "standard.variable.names.problem.descriptor2", + expectedType, boxedType); + } + } + return InspectionGadgetsBundle.message( + "standard.variable.names.problem.descriptor", expectedType); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new StandardVariableNamesVisitor(); + } + + private class StandardVariableNamesVisitor + extends BaseInspectionVisitor { + + @Override + public void visitVariable(@NotNull PsiVariable variable) { + super.visitVariable(variable); + final String variableName = variable.getName(); + final String expectedType = s_expectedTypes.get(variableName); + if (expectedType == null) { + return; + } + final PsiType type = variable.getType(); + final String typeText = type.getCanonicalText(); + if (expectedType.equals(typeText)) { + return; + } + if (PsiUtil.isLanguageLevel5OrHigher(variable)) { + final PsiPrimitiveType unboxedType = + PsiPrimitiveType.getUnboxedType(type); + if (unboxedType != null) { + final String unboxedTypeText = + unboxedType.getCanonicalText(); + if (expectedType.equals(unboxedTypeText)) { + return; + } + } + } + if (ignoreParameterNameSameAsSuper && + isVariableNamedSameAsSuper(variable)) { + return; + } + registerVariableError(variable, variable); + } + + private boolean isVariableNamedSameAsSuper(PsiVariable variable) { + if (!(variable instanceof PsiParameter)) { + return false; + } + final PsiParameter parameter = (PsiParameter)variable; + final PsiElement scope = parameter.getDeclarationScope(); + if (!(scope instanceof PsiMethod)) { + return false; + } + final String variableName = variable.getName(); + final PsiMethod method = (PsiMethod)scope; + final int index = + method.getParameterList().getParameterIndex(parameter); + final PsiMethod[] superMethods = method.findSuperMethods(); + for (PsiMethod superMethod : superMethods) { + final PsiParameter[] parameters = + superMethod.getParameterList().getParameters(); + final PsiParameter overriddenParameter = parameters[index]; + if (variableName.equals(overriddenParameter.getName())) { + return true; + } + } + return false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StaticMethodNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StaticMethodNamingConventionInspectionBase.java new file mode 100644 index 000000000000..98fd3c5166cd --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StaticMethodNamingConventionInspectionBase.java @@ -0,0 +1,92 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiModifier; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class StaticMethodNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 4; + private static final int DEFAULT_MAX_LENGTH = 32; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "static.method.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String methodName = (String)infos[0]; + if (methodName.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "static.method.naming.convention.problem.descriptor.short"); + } + else if (methodName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "static.method.naming.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message( + "static.method.naming.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[a-z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethod(@NotNull PsiMethod method) { + super.visitMethod(method); + if (!method.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + final String name = method.getName(); + if (isValid(name)) { + return; + } + registerMethodError(method, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StaticVariableNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StaticVariableNamingConventionInspectionBase.java new file mode 100644 index 000000000000..55d3c6aa4279 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/StaticVariableNamingConventionInspectionBase.java @@ -0,0 +1,114 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiType; +import com.intellij.util.ui.CheckBox; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ClassUtils; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class StaticVariableNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 5; + private static final int DEFAULT_MAX_LENGTH = 32; + @SuppressWarnings({"PublicField"}) + public boolean checkMutableFinals = false; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("static.variable.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String fieldName = (String)infos[0]; + if (fieldName.length() < getMinLength()) { + return InspectionGadgetsBundle.message("static.variable.naming.convention.problem.descriptor.short"); + } + else if (fieldName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message("static.variable.naming.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message("static.variable.naming.convention.problem.descriptor.regex.mismatch", getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "s_[a-z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public JComponent[] createExtraOptions() { + return new JComponent[]{ + new CheckBox(InspectionGadgetsBundle.message("static.variable.naming.convention.mutable.option"), this, "checkMutableFinals") + }; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitField(@NotNull PsiField field) { + if (!field.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + if (field.hasModifierProperty(PsiModifier.FINAL)) { + if (!checkMutableFinals) { + return; + } + else { + final PsiType type = field.getType(); + if (ClassUtils.isImmutable(type)) { + return; + } + } + } + final String name = field.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + registerFieldError(field, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/TypeParameterNamingConventionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/TypeParameterNamingConventionInspectionBase.java new file mode 100644 index 000000000000..4d0e76be1a60 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/TypeParameterNamingConventionInspectionBase.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiIdentifier; +import com.intellij.psi.PsiTypeParameter; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class TypeParameterNamingConventionInspectionBase extends ConventionInspection { + private static final int DEFAULT_MIN_LENGTH = 1; + private static final int DEFAULT_MAX_LENGTH = 1; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "type.parameter.naming.convention.display.name"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String parameterName = (String)infos[0]; + if (parameterName.length() < getMinLength()) { + return InspectionGadgetsBundle.message( + "type.parameter.naming.convention.problem.descriptor.short"); + } + else if (parameterName.length() > getMaxLength()) { + return InspectionGadgetsBundle.message( + "type.parameter.naming.convention.problem.descriptor.long"); + } + return InspectionGadgetsBundle.message( + "enumerated.class.naming.convention.problem.descriptor.regex.mismatch", + getRegex()); + } + + @Override + protected String getDefaultRegex() { + return "[A-Z][A-Za-z\\d]*"; + } + + @Override + protected int getDefaultMinLength() { + return DEFAULT_MIN_LENGTH; + } + + @Override + protected int getDefaultMaxLength() { + return DEFAULT_MAX_LENGTH; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NamingConventionsVisitor(); + } + + private class NamingConventionsVisitor extends BaseInspectionVisitor { + + @Override + public void visitTypeParameter(PsiTypeParameter parameter) { + super.visitTypeParameter(parameter); + final String name = parameter.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; + } + final PsiIdentifier nameIdentifier = parameter.getNameIdentifier(); + if (nameIdentifier == null) { + return; + } + registerError(nameIdentifier, name); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/UpperCaseFieldNameNotConstantInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/UpperCaseFieldNameNotConstantInspectionBase.java new file mode 100644 index 000000000000..83a65feb7fe5 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/naming/UpperCaseFieldNameNotConstantInspectionBase.java @@ -0,0 +1,76 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.naming; + +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiModifier; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class UpperCaseFieldNameNotConstantInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "upper.case.field.name.not.constant.display.name"); + } + + @Override + @NotNull + public String getID() { + return "NonConstantFieldWithUpperCaseName"; + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "upper.case.field.name.not.constant.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UpperCaseFieldNameNotConstantVisitor(); + } + + private static class UpperCaseFieldNameNotConstantVisitor + extends BaseInspectionVisitor { + + @Override + public void visitField(@NotNull PsiField field) { + super.visitField(field); + if (field.hasModifierProperty(PsiModifier.STATIC) && + field.hasModifierProperty(PsiModifier.FINAL)) { + return; + } + final String fieldName = field.getName(); + if (fieldName == null) { + return; + } + if (!fieldName.equals(fieldName.toUpperCase())) { + return; + } + registerFieldError(field); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/OverlyComplexArithmeticExpressionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/OverlyComplexArithmeticExpressionInspectionBase.java new file mode 100644 index 000000000000..aca2c81e4546 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/OverlyComplexArithmeticExpressionInspectionBase.java @@ -0,0 +1,156 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.numeric; + +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TypeUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +public class OverlyComplexArithmeticExpressionInspectionBase extends BaseInspection { + protected static final Set arithmeticTokens = + new HashSet(5); + private static final int TERM_LIMIT = 6; + /** + * @noinspection PublicField + */ + public int m_limit = TERM_LIMIT; //this is public for the DefaultJDOMExternalizer thingy + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "overly.complex.arithmetic.expression.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "overly.complex.arithmetic.expression.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new OverlyComplexArithmeticExpressionVisitor(); + } + + private class OverlyComplexArithmeticExpressionVisitor + extends BaseInspectionVisitor { + + @Override + public void visitBinaryExpression( + @NotNull PsiBinaryExpression expression) { + super.visitBinaryExpression(expression); + checkExpression(expression); + } + + @Override + public void visitPrefixExpression( + @NotNull PsiPrefixExpression expression) { + super.visitPrefixExpression(expression); + checkExpression(expression); + } + + @Override + public void visitParenthesizedExpression( + PsiParenthesizedExpression expression) { + super.visitParenthesizedExpression(expression); + checkExpression(expression); + } + + private void checkExpression(PsiExpression expression) { + if (isParentArithmetic(expression)) { + return; + } + if (!isArithmetic(expression)) { + return; + } + final int numTerms = countTerms(expression); + if (numTerms <= m_limit) { + return; + } + registerError(expression); + } + + private int countTerms(PsiExpression expression) { + if (!isArithmetic(expression)) { + return 1; + } + if (expression instanceof PsiBinaryExpression) { + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression)expression; + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + return countTerms(lhs) + countTerms(rhs); + } + else if (expression instanceof PsiPrefixExpression) { + final PsiPrefixExpression prefixExpression = + (PsiPrefixExpression)expression; + final PsiExpression operand = prefixExpression.getOperand(); + return countTerms(operand); + } + else if (expression instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = + (PsiParenthesizedExpression)expression; + final PsiExpression contents = parenthesizedExpression.getExpression(); + return countTerms(contents); + } + return 1; + } + + private boolean isParentArithmetic(PsiExpression expression) { + final PsiElement parent = expression.getParent(); + return parent instanceof PsiExpression && isArithmetic((PsiExpression)parent); + } + + private boolean isArithmetic(PsiExpression expression) { + if (expression instanceof PsiBinaryExpression) { + final PsiType type = expression.getType(); + if (TypeUtils.isJavaLangString(type)) { + return false; //ignore string concatenations + } + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression)expression; + return arithmeticTokens.contains(binaryExpression.getOperationTokenType()); + } + else if (expression instanceof PsiPrefixExpression) { + final PsiPrefixExpression prefixExpression = + (PsiPrefixExpression)expression; + return arithmeticTokens.contains(prefixExpression.getOperationTokenType()); + } + else if (expression instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = + (PsiParenthesizedExpression)expression; + final PsiExpression contents = + parenthesizedExpression.getExpression(); + return isArithmetic(contents); + } + return false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/DynamicRegexReplaceableByCompiledPatternInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/DynamicRegexReplaceableByCompiledPatternInspectionBase.java new file mode 100644 index 000000000000..86e5f9c3cfe2 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/DynamicRegexReplaceableByCompiledPatternInspectionBase.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.performance; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.HashSet; + +public class DynamicRegexReplaceableByCompiledPatternInspectionBase extends BaseInspection { + @NonNls + protected static final Collection regexMethodNames = new HashSet(4); + static { + regexMethodNames.add("matches"); + regexMethodNames.add("replaceFirst"); + regexMethodNames.add("replaceAll"); + regexMethodNames.add("split"); + } + + @Override + @Nls + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "dynamic.regex.replaceable.by.compiled.pattern.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "dynamic.regex.replaceable.by.compiled.pattern.problem.descriptor"); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new DynamicRegexReplaceableByCompiledPatternVisitor(); + } + + private static class DynamicRegexReplaceableByCompiledPatternVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + if (!isCallToRegexMethod(expression)) { + return; + } + registerMethodCallError(expression); + } + + + private static boolean isCallToRegexMethod(PsiMethodCallExpression expression) { + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final String name = methodExpression.getReferenceName(); + if (!regexMethodNames.contains(name)) { + return false; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + for (PsiExpression argument : arguments) { + if (!PsiUtil.isConstantExpression(argument)) { + return false; + } + } + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return false; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) { + return false; + } + final String className = containingClass.getQualifiedName(); + return CommonClassNames.JAVA_LANG_STRING.equals(className); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/ToArrayCallWithZeroLengthArrayArgumentInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/ToArrayCallWithZeroLengthArrayArgumentInspectionBase.java new file mode 100644 index 000000000000..51bee0da98c0 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/ToArrayCallWithZeroLengthArrayArgumentInspectionBase.java @@ -0,0 +1,105 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.performance; + +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.CollectionUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class ToArrayCallWithZeroLengthArrayArgumentInspectionBase extends BaseInspection { + @Override + @Nls + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "to.array.call.with.zero.length.array.argument.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + final PsiExpression argument = (PsiExpression)infos[1]; + return InspectionGadgetsBundle.message( + "to.array.call.with.zero.length.array.argument.problem.descriptor", + argument.getText()); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ToArrayCallWithZeroLengthArrayArgument(); + } + + private static class ToArrayCallWithZeroLengthArrayArgument extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + @NonNls final String methodName = methodExpression.getReferenceName(); + if (!"toArray".equals(methodName)) { + return; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 1) { + return; + } + final PsiExpression argument = arguments[0]; + final PsiType type = argument.getType(); + if (!(type instanceof PsiArrayType)) { + return; + } + if (type.getArrayDimensions() != 1) { + return; + } + if (argument instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)argument; + final PsiElement element = referenceExpression.resolve(); + if (!(element instanceof PsiField)) { + return; + } + final PsiField field = (PsiField)element; + if (!CollectionUtils.isConstantEmptyArray(field)) { + return; + } + } + else if (!ExpressionUtils.isZeroLengthArrayConstruction(argument)) { + return; + } + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (!InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_UTIL_COLLECTION)) { + return; + } + registerMethodCallError(expression, expression, argument); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspectionBase.java new file mode 100644 index 000000000000..ede8b3abcdaa --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspectionBase.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.serialization; + +import com.intellij.psi.*; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ClassUtils; +import com.siyeh.ig.psiutils.SerializationUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ExternalizableWithoutPublicNoArgConstructorInspectionBase extends BaseInspection { + @Nullable + protected static PsiMethod getNoArgConstructor(PsiClass aClass) { + final PsiMethod[] constructors = aClass.getConstructors(); + for (PsiMethod constructor : constructors) { + final PsiParameterList parameterList = constructor.getParameterList(); + if (parameterList.getParametersCount() == 0) { + return constructor; + } + } + return null; + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("externalizable.without.public.no.arg.constructor.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("externalizable.without.public.no.arg.constructor.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ExternalizableWithoutPublicNoArgConstructorVisitor(); + } + + private static class ExternalizableWithoutPublicNoArgConstructorVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (aClass.isInterface() || aClass.isEnum() || aClass.isAnnotationType() || aClass instanceof PsiTypeParameter) { + return; + } + if (aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + return; + } + if (!isExternalizable(aClass)) { + return; + } + final PsiMethod constructor = getNoArgConstructor(aClass); + if (constructor == null) { + if (aClass.hasModifierProperty(PsiModifier.PUBLIC)) { + return; + } + } else { + if (constructor.hasModifierProperty(PsiModifier.PUBLIC)) { + return; + } + } + if (SerializationUtils.hasWriteReplace(aClass)) { + return; + } + registerClassError(aClass, aClass, constructor); + } + + private static boolean isExternalizable(PsiClass aClass) { + final PsiClass externalizableClass = ClassUtils.findClass("java.io.Externalizable", aClass); + return externalizableClass != null && aClass.isInheritor(externalizableClass, true); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/NonSerializableFieldInSerializableClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/NonSerializableFieldInSerializableClassInspectionBase.java new file mode 100644 index 000000000000..e2c6c3cba44c --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/NonSerializableFieldInSerializableClassInspectionBase.java @@ -0,0 +1,95 @@ +/* + * Copyright 2006-2012 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.serialization; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.psi.PsiAnonymousClass; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiModifier; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.fixes.AddToIgnoreIfAnnotatedByListQuickFix; +import com.siyeh.ig.psiutils.SerializationUtils; +import com.siyeh.ig.ui.ExternalizableStringSet; +import org.jetbrains.annotations.NotNull; + +public class NonSerializableFieldInSerializableClassInspectionBase extends SerializableInspectionBase { + + @SuppressWarnings({"PublicField"}) + public final ExternalizableStringSet ignorableAnnotations = new ExternalizableStringSet(); + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "non.serializable.field.in.serializable.class.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "non.serializable.field.in.serializable.class.problem.descriptor"); + } + + @NotNull + @Override + protected InspectionGadgetsFix[] buildFixes(Object... infos) { + final PsiField field = (PsiField)infos[0]; + return AddToIgnoreIfAnnotatedByListQuickFix.build(field, ignorableAnnotations); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NonSerializableFieldInSerializableClassVisitor(); + } + + private class NonSerializableFieldInSerializableClassVisitor extends BaseInspectionVisitor { + + @Override + public void visitField(@NotNull PsiField field) { + if (field.hasModifierProperty(PsiModifier.TRANSIENT) || field.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + final PsiClass aClass = field.getContainingClass(); + if (aClass == null) { + return; + } + if (ignoreAnonymousInnerClasses && aClass instanceof PsiAnonymousClass) { + return; + } + if (!SerializationUtils.isSerializable(aClass)) { + return; + } + if (SerializationUtils.isProbablySerializable(field.getType())) { + return; + } + final boolean hasWriteObject = SerializationUtils.hasWriteObject(aClass); + if (hasWriteObject) { + return; + } + if (isIgnoredSubclass(aClass)) { + return; + } + if (AnnotationUtil.isAnnotated(field, ignorableAnnotations)) { + return; + } + registerFieldError(field, field); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableHasSerialVersionUIDFieldInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableHasSerialVersionUIDFieldInspectionBase.java new file mode 100644 index 000000000000..df06cf01f1e8 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableHasSerialVersionUIDFieldInspectionBase.java @@ -0,0 +1,90 @@ +/* + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.serialization; + +import com.intellij.psi.*; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.fixes.AddSerialVersionUIDFix; +import com.siyeh.ig.psiutils.SerializationUtils; +import org.intellij.lang.annotations.Pattern; +import org.jetbrains.annotations.NotNull; + +public class SerializableHasSerialVersionUIDFieldInspectionBase extends SerializableInspectionBase { + + @Pattern("[a-zA-Z_0-9.-]+") + @Override + @NotNull + public String getID() { + return "serial"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "serializable.class.without.serialversionuid.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "serializable.class.without.serialversionuid.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new AddSerialVersionUIDFix(); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new SerializableHasSerialVersionUIDFieldVisitor(); + } + + private class SerializableHasSerialVersionUIDFieldVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isEnum()) { + return; + } + if (aClass instanceof PsiTypeParameter || aClass instanceof PsiEnumConstantInitializer) { + return; + } + if (ignoreAnonymousInnerClasses && aClass instanceof PsiAnonymousClass) { + return; + } + final PsiField serialVersionUIDField = aClass.findFieldByName(HardcodedMethodConstants.SERIAL_VERSION_UID, false); + if (serialVersionUIDField != null) { + return; + } + if (!SerializationUtils.isSerializable(aClass)) { + return; + } + if (SerializationUtils.hasWriteReplace(aClass)) { + return; + } + if (isIgnoredSubclass(aClass)) { + return; + } + registerClassError(aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableHasSerializationMethodsInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableHasSerializationMethodsInspectionBase.java new file mode 100644 index 000000000000..3d9aaef4e71f --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableHasSerializationMethodsInspectionBase.java @@ -0,0 +1,96 @@ +/* + * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.serialization; + +import com.intellij.psi.PsiAnonymousClass; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiEnumConstantInitializer; +import com.intellij.psi.PsiTypeParameter; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.SerializationUtils; +import org.jetbrains.annotations.NotNull; + +public class SerializableHasSerializationMethodsInspectionBase + extends SerializableInspectionBase { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "serializable.has.serialization.methods.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final boolean hasReadObject = ((Boolean)infos[0]).booleanValue(); + final boolean hasWriteObject = ((Boolean)infos[1]).booleanValue(); + if (!hasReadObject && !hasWriteObject) { + return InspectionGadgetsBundle.message( + "serializable.has.serialization.methods.problem.descriptor"); + } + else if (hasReadObject) { + return InspectionGadgetsBundle.message( + "serializable.has.serialization.methods.problem.descriptor1"); + } + else { + return InspectionGadgetsBundle.message( + "serializable.has.serialization.methods.problem.descriptor2"); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new SerializableHasSerializationMethodsVisitor(); + } + + private class SerializableHasSerializationMethodsVisitor + extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so it doesn't drill down + if (aClass.isInterface() || aClass.isAnnotationType() || + aClass.isEnum()) { + return; + } + if (aClass instanceof PsiTypeParameter || + aClass instanceof PsiEnumConstantInitializer) { + return; + } + if (ignoreAnonymousInnerClasses && + aClass instanceof PsiAnonymousClass) { + return; + } + if (!SerializationUtils.isSerializable(aClass)) { + return; + } + final boolean hasReadObject = + SerializationUtils.hasReadObject(aClass); + final boolean hasWriteObject = + SerializationUtils.hasWriteObject(aClass); + if (hasWriteObject && hasReadObject) { + return; + } + if (isIgnoredSubclass(aClass)) { + return; + } + registerClassError(aClass, Boolean.valueOf(hasReadObject), + Boolean.valueOf(hasWriteObject)); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldInspectionBase.java new file mode 100644 index 000000000000..439cf753ba66 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldInspectionBase.java @@ -0,0 +1,56 @@ +/* + * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.serialization; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.fixes.AddSerialVersionUIDFix; +import org.jetbrains.annotations.NotNull; + +public class SerializableInnerClassHasSerialVersionUIDFieldInspectionBase + extends SerializableInspectionBase { + + @Override + @NotNull + public String getID() { + return "SerializableNonStaticInnerClassWithoutSerialVersionUID"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "serializable.inner.class.has.serial.version.uid.field.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "serializable.inner.class.has.serial.version.uid.field.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new AddSerialVersionUIDFix(); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new SerializableInnerClassHasSerialVersionUIDFieldVisitor(this); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldVisitor.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldVisitor.java new file mode 100644 index 000000000000..9f3c83d110ec --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassHasSerialVersionUIDFieldVisitor.java @@ -0,0 +1,79 @@ +/* + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.serialization; + +import com.intellij.psi.PsiAnonymousClass; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiModifier; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.SerializationUtils; +import org.jetbrains.annotations.NotNull; + +class SerializableInnerClassHasSerialVersionUIDFieldVisitor + extends BaseInspectionVisitor { + + private final SerializableInspectionBase inspection; + + public SerializableInnerClassHasSerialVersionUIDFieldVisitor( + SerializableInspectionBase inspection) { + this.inspection = inspection; + } + + @Override + public void visitClass(@NotNull PsiClass aClass) { + // no call to super, so it doesn't drill down + if (aClass.isInterface() || aClass.isAnnotationType() || + aClass.isEnum()) { + return; + } + if (inspection.ignoreAnonymousInnerClasses && + aClass instanceof PsiAnonymousClass) { + return; + } + if (hasSerialVersionUIDField(aClass)) { + return; + } + final PsiClass containingClass = aClass.getContainingClass(); + if (containingClass == null) { + return; + } + if (aClass.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + if (!SerializationUtils.isSerializable(aClass)) { + return; + } + if (inspection.isIgnoredSubclass(aClass)) { + return; + } + registerClassError(aClass); + } + + private static boolean hasSerialVersionUIDField(PsiClass aClass) { + final PsiField[] fields = aClass.getFields(); + boolean hasSerialVersionUID = false; + for (PsiField field : fields) { + final String fieldName = field.getName(); + if (HardcodedMethodConstants.SERIAL_VERSION_UID.equals( + fieldName)) { + hasSerialVersionUID = true; + } + } + return hasSerialVersionUID; + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassWithNonSerializableOuterClassInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassWithNonSerializableOuterClassInspectionBase.java new file mode 100644 index 000000000000..8ea9c1c3ac65 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassWithNonSerializableOuterClassInspectionBase.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.serialization; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class SerializableInnerClassWithNonSerializableOuterClassInspectionBase + extends SerializableInspectionBase { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "serializable.inner.class.with.non.serializable.outer.class.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "serializable.inner.class.with.non.serializable.outer.class.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new SerializableInnerClassWithNonSerializableOuterClassVisitor(this); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassWithNonSerializableOuterClassVisitor.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassWithNonSerializableOuterClassVisitor.java new file mode 100644 index 000000000000..a3e9ee3851b6 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInnerClassWithNonSerializableOuterClassVisitor.java @@ -0,0 +1,63 @@ +/* + * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.serialization; + +import com.intellij.psi.PsiAnonymousClass; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiModifier; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.SerializationUtils; +import org.jetbrains.annotations.NotNull; + +class SerializableInnerClassWithNonSerializableOuterClassVisitor + extends BaseInspectionVisitor { + + private final SerializableInspectionBase inspection; + + public SerializableInnerClassWithNonSerializableOuterClassVisitor( + SerializableInspectionBase inspection) { + this.inspection = inspection; + } + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (aClass.isInterface() || aClass.isAnnotationType() || + aClass.isEnum()) { + return; + } + if (inspection.ignoreAnonymousInnerClasses && + aClass instanceof PsiAnonymousClass) { + return; + } + final PsiClass containingClass = aClass.getContainingClass(); + if (containingClass == null) { + return; + } + if (aClass.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + if (!SerializationUtils.isSerializable(aClass)) { + return; + } + if (SerializationUtils.isSerializable(containingClass)) { + return; + } + if (inspection.isIgnoredSubclass(aClass)) { + return; + } + registerClassError(aClass); + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInspectionBase.java new file mode 100644 index 000000000000..498719c80d13 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/serialization/SerializableInspectionBase.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.serialization; + +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.PsiClass; +import com.intellij.psi.util.InheritanceUtil; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.psiutils.SerializationUtils; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +public abstract class SerializableInspectionBase extends BaseInspection { + private static final JComponent[] EMPTY_COMPONENT_ARRAY = {}; + @SuppressWarnings({"PublicField"}) + public boolean ignoreAnonymousInnerClasses = false; + @Deprecated @SuppressWarnings({"PublicField"}) + public String superClassString = "java.awt.Component"; + protected List superClassList = new ArrayList(); + + @Override + public void readSettings(@NotNull Element node) throws InvalidDataException { + super.readSettings(node); + parseString(superClassString, superClassList); + } + + @Override + public void writeSettings(@NotNull Element node) throws WriteExternalException { + superClassString = formatString(superClassList); + super.writeSettings(node); + } + + protected JComponent[] createAdditionalOptions() { + return EMPTY_COMPONENT_ARRAY; + } + + protected boolean isIgnoredSubclass(PsiClass aClass) { + if (SerializationUtils.isDirectlySerializable(aClass)) { + return false; + } + for (String superClassName : superClassList) { + if (InheritanceUtil.isInheritor(aClass, superClassName)) { + return true; + } + } + return false; + } + + @Override + public String getAlternativeID() { + return "serial"; + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/ChainedMethodCallInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/ChainedMethodCallInspectionBase.java new file mode 100644 index 000000000000..b06cf7e79d69 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/ChainedMethodCallInspectionBase.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.style; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.NotNull; + +public class ChainedMethodCallInspectionBase extends BaseInspection { + @SuppressWarnings("PublicField") + public boolean m_ignoreFieldInitializations = true; + @SuppressWarnings("PublicField") + public boolean m_ignoreThisSuperCalls = true; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("chained.method.call.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("chained.method.call.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ChainedMethodCallVisitor(); + } + + private class ChainedMethodCallVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression reference = expression.getMethodExpression(); + final PsiExpression qualifier = reference.getQualifierExpression(); + if (qualifier == null) { + return; + } + if (!isCallExpression(qualifier)) { + return; + } + if (m_ignoreFieldInitializations) { + final PsiElement field = PsiTreeUtil.getParentOfType(expression, PsiField.class); + if (field != null) { + return; + } + } + if (m_ignoreThisSuperCalls) { + final PsiExpressionList expressionList = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class); + if (expressionList != null) { + final PsiElement parent = expressionList.getParent(); + if (ExpressionUtils.isConstructorInvocation(parent)) { + return; + } + } + } + registerMethodCallError(expression); + } + + private boolean isCallExpression(PsiExpression expression) { + expression = ParenthesesUtils.stripParentheses(expression); + return expression instanceof PsiMethodCallExpression || expression instanceof PsiNewExpression; + } + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/NestedMethodCallInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/NestedMethodCallInspectionBase.java new file mode 100644 index 000000000000..484c1f8fefe0 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/NestedMethodCallInspectionBase.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.style; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.jetbrains.annotations.NotNull; + +public class NestedMethodCallInspectionBase extends BaseInspection { + /** + * @noinspection PublicField + */ + public boolean m_ignoreFieldInitializations = true; + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("nested.method.call.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("nested.method.call.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new NestedMethodCallVisitor(); + } + + @Override + protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { + return true; + } + + private class NestedMethodCallVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + PsiExpression outerExpression = expression; + while (outerExpression != null && outerExpression.getParent() instanceof PsiExpression) { + outerExpression = (PsiExpression)outerExpression.getParent(); + } + if (outerExpression == null) { + return; + } + final PsiElement parent = outerExpression.getParent(); + if (!(parent instanceof PsiExpressionList)) { + return; + } + final PsiElement grandParent = parent.getParent(); + if (!(grandParent instanceof PsiCallExpression)) { + return; + } + if (ExpressionUtils.isConstructorInvocation(grandParent)) { + //ignore nested method calls at the start of a constructor, + //where they can't be extracted + return; + } + if (m_ignoreFieldInitializations) { + final PsiElement field = PsiTreeUtil.getParentOfType(expression, PsiField.class); + if (field != null) { + return; + } + } + registerMethodCallError(expression); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SizeReplaceableByIsEmptyInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SizeReplaceableByIsEmptyInspectionBase.java new file mode 100644 index 000000000000..f43b9d188bcc --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SizeReplaceableByIsEmptyInspectionBase.java @@ -0,0 +1,213 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.style; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.OrderedSet; +import com.siyeh.HardcodedMethodConstants; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ComparisonUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SizeReplaceableByIsEmptyInspectionBase extends BaseInspection { + @SuppressWarnings({"PublicField"}) + public boolean ignoreNegations = false; + @SuppressWarnings("PublicField") + public OrderedSet ignoredTypes = new OrderedSet(); + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("size.replaceable.by.isempty.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("expression.can.be.replaced.problem.descriptor", infos[0]); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new SizeReplaceableByIsEmptyVisitor(); + } + + protected static class SizeReplaceableByIsEmptyFix + extends InspectionGadgetsFix { + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("size.replaceable.by.isempty.quickfix"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)descriptor.getPsiElement(); + PsiExpression operand = binaryExpression.getLOperand(); + if (!(operand instanceof PsiMethodCallExpression)) { + operand = binaryExpression.getROperand(); + } + if (!(operand instanceof PsiMethodCallExpression)) { + return; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)operand; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + if (qualifierExpression == null) { + return; + } + @NonNls String newExpression = qualifierExpression.getText(); + final IElementType tokenType = binaryExpression.getOperationTokenType(); + if (!JavaTokenType.EQEQ.equals(tokenType)) { + newExpression = '!' + newExpression; + } + newExpression += ".isEmpty()"; + replaceExpression(binaryExpression, newExpression); + } + } + + private class SizeReplaceableByIsEmptyVisitor extends BaseInspectionVisitor { + + @Override + public void visitBinaryExpression(PsiBinaryExpression expression) { + super.visitBinaryExpression(expression); + final PsiExpression rhs = expression.getROperand(); + if (rhs == null) { + return; + } + if (!ComparisonUtils.isComparison(expression)) { + return; + } + final PsiExpression lhs = expression.getLOperand(); + if (lhs instanceof PsiMethodCallExpression) { + final String replacementIsEmptyCall = getReplacementIsEmptyCall(lhs, rhs, false, expression.getOperationTokenType()); + if (replacementIsEmptyCall != null) { + registerError(expression, replacementIsEmptyCall); + } + } + else if (rhs instanceof PsiMethodCallExpression) { + final String replacementIsEmptyCall = getReplacementIsEmptyCall(rhs, lhs, true, expression.getOperationTokenType()); + if (replacementIsEmptyCall != null) { + registerError(expression, replacementIsEmptyCall); + } + } + } + + @Nullable + private String getReplacementIsEmptyCall(PsiExpression lhs, PsiExpression rhs, boolean flipped, IElementType tokenType) { + final PsiMethodCallExpression callExpression = (PsiMethodCallExpression)lhs; + final String isEmptyCall = getIsEmptyCall(callExpression); + if (isEmptyCall == null) { + return null; + } + final Object object = ExpressionUtils.computeConstantExpression(rhs); + if (!(object instanceof Integer)) { + return null; + } + final Integer integer = (Integer)object; + final int constant = integer.intValue(); + if (constant != 0) { + return null; + } + if (JavaTokenType.EQEQ.equals(tokenType)) { + return isEmptyCall; + } + if (ignoreNegations) { + return null; + } + if (JavaTokenType.NE.equals(tokenType)) { + return '!' + isEmptyCall; + } + else if (flipped) { + if (JavaTokenType.LT.equals(tokenType)) { + return '!' + isEmptyCall; + } + } + else if (JavaTokenType.GT.equals(tokenType)) { + return '!' + isEmptyCall; + } + return null; + } + + @Nullable + private String getIsEmptyCall(PsiMethodCallExpression callExpression) { + final PsiReferenceExpression methodExpression = callExpression.getMethodExpression(); + final String referenceName = methodExpression.getReferenceName(); + if (!HardcodedMethodConstants.SIZE.equals(referenceName) && + !HardcodedMethodConstants.LENGTH.equals(referenceName)) { + return null; + } + final PsiExpressionList argumentList = callExpression.getArgumentList(); + final PsiExpression[] expressions = argumentList.getExpressions(); + if (expressions.length != 0) { + return null; + } + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + if (qualifierExpression == null) { + return null; + } + final PsiType type = qualifierExpression.getType(); + if (!(type instanceof PsiClassType)) { + return null; + } + final PsiClassType classType = (PsiClassType)type; + final PsiClass aClass = classType.resolve(); + if (aClass == null) { + return null; + } + if (PsiTreeUtil.isAncestor(aClass, callExpression, true)) { + return null; + } + for (String ignoredType : ignoredTypes) { + if (InheritanceUtil.isInheritor(aClass, ignoredType)) { + return null; + } + } + final PsiMethod[] methods = aClass.findMethodsByName("isEmpty", true); + for (PsiMethod method : methods) { + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() == 0) { + return qualifierExpression.getText() + ".isEmpty()"; + } + } + return null; + } + } + + @Override + @Nullable + protected InspectionGadgetsFix buildFix(Object... infos) { + return new SizeReplaceableByIsEmptyFix(); + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedInnerClassAccessInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedInnerClassAccessInspectionBase.java new file mode 100644 index 000000000000..ced77f835ea6 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedInnerClassAccessInspectionBase.java @@ -0,0 +1,135 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.style; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +public class UnqualifiedInnerClassAccessInspectionBase extends BaseInspection { + @SuppressWarnings({"PublicField"}) + public boolean ignoreReferencesToLocalInnerClasses = false; + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("unqualified.inner.class.access.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("unqualified.inner.class.access.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UnqualifiedInnerClassAccessVisitor(); + } + + protected static class ReferenceCollector extends JavaRecursiveElementVisitor { + + private final String name; + private final boolean onDemand; + private final Set references = new HashSet(); + + ReferenceCollector(String name, boolean onDemand) { + this.name = name; + this.onDemand = onDemand; + } + + @Override + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + super.visitReferenceElement(reference); + if (reference.isQualified()) { + return; + } + final PsiElement target = reference.resolve(); + if (!(target instanceof PsiClass)) { + return; + } + final PsiClass aClass = (PsiClass)target; + if (!onDemand) { + final String qualifiedName = aClass.getQualifiedName(); + if (name.equals(qualifiedName)) { + references.add(reference); + } + return; + } + final PsiClass containingClass = aClass.getContainingClass(); + if (containingClass == null) { + return; + } + final String qualifiedName = containingClass.getQualifiedName(); + if (name.equals(qualifiedName)) { + references.add(reference); + } + } + + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + visitReferenceElement(expression); + } + + public Collection getReferences() { + return references; + } + } + + private class UnqualifiedInnerClassAccessVisitor extends BaseInspectionVisitor { + + @Override + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + super.visitReferenceElement(reference); + if (reference.isQualified()) { + return; + } + final PsiElement target = reference.resolve(); + if (!(target instanceof PsiClass)) { + return; + } + final PsiClass aClass = (PsiClass)target; + final PsiClass containingClass = aClass.getContainingClass(); + if (containingClass == null) { + return; + } + if (ignoreReferencesToLocalInnerClasses) { + if (PsiTreeUtil.isAncestor(containingClass, reference, true)) { + return; + } + final PsiClass referenceClass = PsiTreeUtil.getParentOfType(reference, PsiClass.class); + if (referenceClass != null && referenceClass.isInheritor(containingClass, true)) { + return; + } + } + registerError(reference, containingClass.getName()); + } + + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + visitReferenceElement(expression); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/ExtendsThreadInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/ExtendsThreadInspectionBase.java new file mode 100644 index 000000000000..59d1568c3e00 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/ExtendsThreadInspectionBase.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.threading; + +import com.intellij.psi.PsiAnonymousClass; +import com.intellij.psi.PsiClass; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import org.jetbrains.annotations.NotNull; + +public class ExtendsThreadInspectionBase extends BaseInspection { + @Override + @NotNull + public String getID() { + return "ClassExplicitlyExtendsThread"; + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("extends.thread.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + final PsiClass aClass = (PsiClass)infos[0]; + if (aClass instanceof PsiAnonymousClass) { + return InspectionGadgetsBundle.message("anonymous.extends.thread.problem.descriptor"); + } else { + return InspectionGadgetsBundle.message("extends.thread.problem.descriptor"); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new ExtendsThreadVisitor(); + } + + private static class ExtendsThreadVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (aClass.isInterface() || aClass.isAnnotationType() || aClass.isEnum()) { + return; + } + final PsiClass superClass = aClass.getSuperClass(); + if (superClass == null) { + return; + } + final String superclassName = superClass.getQualifiedName(); + if (!"java.lang.Thread".equals(superclassName)) { + return; + } + registerClassError(aClass, aClass); + } + } +} -- cgit v1.2.3