diff options
author | Tor Norbye <tnorbye@google.com> | 2013-09-11 15:53:32 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2013-09-11 15:53:37 -0700 |
commit | a28de544d7f6bae0c637a1f8175ea613a976c52e (patch) | |
tree | 0b506efac9439f5a06d00e4aef4597c6d49bba3c /plugins/InspectionGadgets/InspectionGadgetsAnalysis | |
parent | c7f983b5dcd0499fc68aacaba043874648932b6b (diff) | |
download | idea-a28de544d7f6bae0c637a1f8175ea613a976c52e.tar.gz |
Snapshot b7cfcd5072e521a95c49cdd58405fa262075116e from idea/132.197 of git://git.jetbrains.org/idea/community.git
b7cfcd5: 2013-09-10 Alexey Kudravtsev - cleanup
98e9b07: 2013-09-10 Alexey Kudravtsev - optimisation: almost always one element set/map
06b1744: 2013-09-10 Alexey Kudravtsev - cleanup static final constants
c2f6153: 2013-09-10 Alexey Kudravtsev - cleanup
df9d3ff: 2013-09-10 Alexey Kudravtsev - IDEA-113331
c502feb: 2013-09-10 Alexey Kudravtsev - cleanup
4790327: 2013-09-09 Alexey Kudravtsev - cleanup
734f361: 2013-09-09 Alexey Kudravtsev - cleanup
4fa0b86: 2013-09-11 Dmitry Avdeev - action moved from platform to IDEA
cade1ac: 2013-09-11 Bas Leijdekkers - don't make quickfix available when it does nothing
e25c86f: 2013-09-11 nik - fixed copying items from 'Dependencies' panel (IDEA-108071)
ba416aa: 2013-09-11 nik - IDEA-108213: ZipException on attempt to extract non-existent jar when building artifact
e55712a: 2013-09-11 nik - show path to incorrect zip-file in error message (IDEA-113123), improved
68d131a: 2013-09-11 Vladislav.Soroka - Gradle: gradle build file can be used to resolve project info instead of gradle project dir
09cdde1: 2013-09-11 nik - JPS bootstrap build libs updated
6e4c9f0: 2013-09-10 nik - xdebugger: mark new properties appeared after step in debugger trees (WI-3066)
7aa6d26: 2013-09-10 nik - jps model: added method to process annotation roots from module and its dependencies
de4cbc7: 2013-09-10 nik - IDEA-112009: Library selection dialog is too small
ba3c923: 2013-09-10 Bas Leijdekkers - IDEA-93530 (bad code green: cannot assign a value to final variable)
ae1a4ca: 2013-09-11 Vassiliy Kudryashov - Lense mode "internal" preview
296e85f: 2013-09-10 Dmitry Jemerov - Merge branch 'master' of git://github.com/asedunov/intellij-community into pull97
816cc8a: 2013-09-10 Bas Leijdekkers - IDEA-93515 (Good code red: variable might not have been initialized)
f3a5bef: 2013-09-10 Gregory.Shrago - IDEA-113281 Ctrl+E for Console History shadows hotkey for Recent
00c7cc6: 2013-09-10 Anna Kozlova - EA-49751 - NPE: TestsOutputConsolePrinter.isRoot
c848db6: 2013-09-10 Anna Kozlova - Poly Expressions
9f5e9d5: 2013-09-10 Vladimir Krivosheev - correct fix WEB-8151
8534bf1: 2013-09-10 peter - cache the default dfaVar type
2ee4c95: 2013-09-10 peter - merge DfaNotNullValue into DfaTypeValue
650794d: 2013-09-10 peter - some findusageability in dfa
a75c57d: 2013-09-10 Alexey Sedunov - Refactor method search classes for better reusability
049514a: 2013-09-10 Vladislav.Soroka - IDEA-50450 Gradle code insight; tasks dsl resolving (incomplete)
8bb28e2: 2013-09-10 Denis Fokin - Cocoa does not like activities while transition in full screen is happening. This fix prevents simultaneous entering in the full screen mode.
c837b30: 2013-09-10 Sergey Evdokimov - IDEA-109771 Incorrent Maven dependency resolution with war classes dependency
eede608: 2013-09-10 Dmitry Avdeev - no icons in wizards
a18ab7c: 2013-09-10 Dmitry Avdeev - cleanup
204e83b: 2013-09-10 Dmitry Avdeev - cleanup
33d61c5: 2013-09-10 Dmitry Avdeev - fixed layout in absence of icons
70a172a: 2013-09-10 Bas Leijdekkers - IDEA-91960 (Type parameter extends final class inspection warns on cases when can not be removed)
f0e77a8: 2013-09-10 Rustam Vishnyakov - Allow to move a standard code style option to another group [CR-IC-2192] (cherry picked from commit 81c4f16)
3748154: 2013-09-10 Max Medvedev - IDEA-113299 Imports removed when writing javadoc
1a6ca29: 2013-09-10 Max Medvedev - IDEA-52424 language injection for GStrings and binary expressions
83237e6: 2013-07-07 Max Medvedev - IDEA-99638 completion in @Language("<caret>")
075e763: 2013-09-10 Aleksey Pivovarov - @NotNull
836e1af: 2013-09-10 Aleksey Pivovarov - Github: Ref -> AtomicReference
2c384d0: 2013-09-10 Aleksey Pivovarov - Github: remove duplication
1d09e47: 2013-09-10 Aleksey Pivovarov - Github: fetch a single branch for diff
3361c53: 2013-09-10 Aleksey Pivovarov - Github: use BorderFactory
0c1a982: 2013-09-10 Aleksey Pivovarov - Github: Nullable
403902b: 2013-09-07 Aleksey Pivovarov - Github: project-level settings
911bea9: 2013-09-10 Vladislav.Soroka - IDEA-113403 Gradle: disable wrapper verification by default and add check for default wrapper files
7434b38: 2013-09-10 Dmitry Avdeev - preferred focused component for wizards
ecf3594: 2013-09-10 peter - static CachedValueManager.getCachedValue(PsiElement, CachedValueProvider) for less verbose cached value storage in psi user data
7d3f4f25: 2013-09-10 peter - some dfa simplifications not affecting tests
9d04224: 2013-09-09 peter - remove unused DelegatingInstructionVisitor
dff5e27: 2013-09-10 Denis Fokin - Windows long paths issue has been fixed, the com.intellij.ui.win.Task was moved from RecentTasks class because of some problems with JNI invocations. Class path now is enclosed by double quotes.
a1e5a9d: 2013-09-10 Ekaterina Tuzova - Merge remote-tracking branch 'origin/master'
dcb78a4: 2013-09-10 Ekaterina Tuzova - Merge remote-tracking branch 'origin/master'
7840bbe: 2013-09-10 Anna Kozlova - deprecate to be able to be used in PsiClass etc
401e730: 2013-09-10 Ekaterina Tuzova - Merge remote-tracking branch 'origin/master'
2fb38cf: 2013-09-10 Anna Kozlova - Merge remote-tracking branch 'origin/master'
6bae9e1: 2013-09-10 Anna Kozlova - optimization
a5ebbff: 2013-09-10 Anna Kozlova - cleanup lambda specifics
a44e101: 2013-09-10 Anna Kozlova - EA-49734 - IAE: GenericsHighlightUtil.checkReferenceTypeArgumentList
73bc4cb: 2013-09-10 Vladimir Krivosheev - auth works now, continue "sync now" button
f590909: 2013-09-10 Dmitry Jemerov - Merge branch 'master' of git://github.com/bashor/intellij-community into pull96
769fd94: 2013-09-10 Ekaterina Tuzova - fixed PY-10714 Reformat of string creates error
da07f0e: 2013-09-10 Konstantin Kolosovsky - Merge branch 'svn1_8_new'
08f3baf: 2013-09-10 Konstantin Kolosovsky - IDEA-112179 PropertiesComponent refactored - duplication removed
cdfb7ae: 2013-09-10 Konstantin Kolosovsky - IDEA-112179 Add/Edit/Delete svn properties
0a36021: 2013-09-10 nik - IDEA-112941: Throwable at com.intellij.openapi.roots.ui.configuration.classpath.ChangeLibraryLevelActionBase$1.run
da397a1: 2013-09-10 peter - add a note on adverse groovy-hotswap effects on serialization (IDEA-61884)
adf5bb7: 2013-09-10 nik - fixed building CE by 64-bit JDK (IDEA-112953)
1b396ef: 2013-09-10 nik - jps build: memory usage optimization
921a2f3: 2013-09-10 Konstantin Bulenkov - mem leak fixed
276c402: 2013-09-10 Dmitry Avdeev - new project wizard
b07de54: 2013-09-10 nik - show path to incorrect zip-file in error message (IDEA-113123)
ab4e6c6: 2013-09-10 nik - remote servers: allow to show additional logs for deployments in 'Servers View' - 2
eb65d57: 2013-09-09 nik - remote servers: allow to show additional logs for deployments in 'Servers View'
faff6c2: 2013-07-07 Max Medvedev - duplicated test
fe0569b: 2013-07-07 Max Medvedev - fix broken jar in test-data
a840eb2: 2013-07-07 Max Medvedev - Groovy shell: support commands
bb2ff42: 2013-07-07 Max Medvedev - Groovy Shell: support classes
a7658c1: 2013-07-07 Max Medvedev - Complete imported classes in GroovyCodeFragment
e53f7be: 2013-07-07 Max Medvedev - NN/Nu
edd7e1e: 2013-07-07 Max Medvedev - support imports in GroovyShell
f203ff2: 2013-07-07 Max Medvedev - support imports in GroovyShell
d45e9cd: 2013-07-07 Max Medvedev - Groovy element factory: create import from statement
59f6256: 2013-07-07 Max Medvedev - IDEA-110179 fix control flow building for if-statement
c9f45b6: 2013-07-07 Max Medvedev - fix GroovyIntentionsBundle
02bf9a6: 2013-07-07 Max Medvedev - IDEA-110178 'convert concatenation to GString' intention supports regexes
8f4f1ac: 2013-07-06 Max Medvedev - fix GroovyEnterHandler
f5bb52d: 2013-07-06 Max Medvedev - IDEA-110178 upper letters
711f61e: 2013-07-06 Max Medvedev - IDEA-110178 'convert string literal to dollar-slashy string' intention
9462203: 2013-07-06 Max Medvedev - IDEA-110178 'convert string literal to regex' intention should convert dollar-slashy regexes
c53a519: 2013-07-06 Max Medvedev - cleanup GrStringUtil
7134b63: 2013-07-06 Max Medvedev - IDEA-110178 'convert string literal to regex' intention
60b2f0a: 2013-07-06 Max Medvedev - IDEA-109053 Convert to single-line string fixed
1dea12c: 2013-09-10 Vladimir Krivosheev - fix fetch initial "sync now" button
3cc0794: 2013-09-09 Vladimir Krivosheev - add note about SkipDefaultValuesSerializationFilters
67ecfc0: 2013-09-09 Vladimir Krivosheev - ApplicationImpl — cleanup getExtensions ics — commit under progress
4cae157: 2013-09-09 Anna Kozlova - more specific: lambda extended initial fix (IDEA-113357)
0195034: 2013-09-09 Anna Kozlova - lambda -> method ref: reject unresolved lambdas
f1b399b: 2013-09-09 Alexander Lobas - RenderServer
4252a50: 2013-09-09 Vladimir Krivosheev - SingleAlarm — should use ModalityState.NON_MODAL
a5ca071: 2013-09-09 Konstantin Kolosovsky - IDEA-112179 "Set property" action implemented
2fa6ff6: 2013-09-09 Bas Leijdekkers - test for IDEA-53184
b57f5e1: 2013-09-09 Konstantin Bulenkov - WI-19949 Parent class methods aren't shown after second Command-F12
a2beba8: 2013-09-09 Zalim Bashorov - When we don't have any dependency(after f4a27e8) we should use ModificationTracker.NEVER_CHANGED instead empty array.
5154748: 2013-09-09 Konstantin Kolosovsky - IDEA-112181 Handling readonly files with or without "svn:needs-lock" property
55a5f4c: 2013-09-09 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
e6f6c94: 2013-09-09 Vladimir Krivosheev - WEB-6780 JS Debugger / Variables: "Jump to Source" not working: This action should be available only for object or functions.
cd02963: 2013-09-09 Konstantin Kolosovsky - IDEA-112181 Lock data parsing for "svn info" command
66b72f8: 2013-09-09 Bas Leijdekkers - fix test and add test for IOE
bb9b1f5: 2013-09-09 peter - IDEA-113391 Edit Method Contract intention for library methods
b77731d: 2013-09-09 Konstantin Kolosovsky - IDEA-112181 Lock/Unlock actions implemented
bc8cb84: 2013-09-09 Konstantin Kolosovsky - IDEA-94942 Utility methods to parse svn command line warnings
b45114d: 2013-09-09 Dmitry Trofimov - Possibility to select Maya.app on Mac in add interpreter dialog.
631b361: 2013-09-09 Sergey Evdokimov - Completion of dependency should honor <dependencyManagement> section
0fcbc91: 2013-09-09 Roman Shevchenko - platform: correct formatting in the missing framework library validator
660731f: 2013-09-09 Bas Leijdekkers - use TypeConversionUtil.erasure() instead of reinventing the wheel badly
0fcec29: 2013-09-09 Bas Leijdekkers - prevent IOE on array initializer without array type (e.g. Map<String, String> m = {})
5d70c64: 2013-09-09 Fedor Korotkov - Reverted 482c517
ea4b4f2: 2013-09-09 Vladimir Krivosheev - continue WEB-1171 javascript live console: execution enabled condition
54872b0: 2013-09-09 Anna Kozlova - accept interfaces as main classes for java 8 (IDEA-113340)
c9fc494: 2013-09-09 Anna Kozlova - pull up: ensure that default won't be inserted for ll < 1.8 (IDEA-113323)
c51363b: 2013-09-09 Vladimir Krivosheev - we should not call execute action if completion variant not focused
7c5f486: 2013-09-09 Dmitry Trofimov - Disposable parent can be null.
1e9a0d8: 2013-09-09 nik - xdebugger: simplified calling old deprecated method & javadocs added
2e3c259: 2013-09-09 nik - notnullification
0aa3d9d: 2013-09-09 Vladimir Krivosheev - continue WEB-1171 javascript live console: add history actions
ac12238: 2013-09-09 Vladimir Krivosheev - add Copyright
c7d7080: 2013-09-08 Alex Ruiz - Minor changes to NewProjectCheckoutListener to reuse its code.
8d29e4f: 2013-09-09 Sergey Simonchik - XDebugProcess method rename: createLayouter -> createTabLayouter
f58251b: 2013-09-09 Sergey Simonchik - XDebugProcess.registerAdditionalContent method restored to keep compatibility with IDEA 12 API
286cdc3: 2013-09-09 Anna Kozlova - anonym -> lambda: unique param names (IDEA-113338)
238e311: 2013-09-09 Vladimir Krivosheev - prefer to use interface LanguageConsoleView instead of impl class
d75838a: 2013-09-06 Vladimir Krivosheev - continue WEB-1171 javascript live console — works now, but UI is ugly
9f054b0: 2013-09-09 Sergey Simonchik - XDebug layout API improvement: XDebugProcess.registerAdditionalContent moved to XDebugLayouter
7cc0d20: 2013-09-08 Vladimir Krivosheev - schedule commit&push
4d9e998: 2013-09-08 Vladimir Krivosheev - move SingleAlarm to platform-impl
4d840cf: 2013-09-08 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
134a7fa: 2013-09-08 Fedor Korotkov - WEB-8731 HTML parser breaks with unquoted complex attributes
482c517: 2013-09-08 Fedor Korotkov - WEB-8397 Incorrect "element is not allowed here" inspection
3bbc3f7: 2013-09-08 Dmitry Trofimov - Implement get variable for console variables view.
12e32a8: 2013-09-08 Bas Leijdekkers - IDEA-96419 ("Externalizable class without public no-arg constructor" inspection options)
2d25b3b: 2013-09-08 Bas Leijdekkers - DRY
8941cf8: 2013-09-08 Bas Leijdekkers - IDEA-113298 (replace assertTrue with assertFalse removes message)
72c972f: 2013-09-07 Max Medvedev - auto inject RegExp lang to reg-exp operator argeuments
e7fa284: 2013-09-07 Max Medvedev - IDEA-113346 False negative in groovy withStream closure
0b795be: 2013-09-07 Bas Leijdekkers - IDEA-85224 (Inspection "non thread-safe static field access" gives false +ve for static initializers)
28a1e72: 2013-09-07 Bas Leijdekkers - cleanup
8bc5fbf: 2013-06-12 Max Medvedev - IDEA-111780 Extract method from static closure should create static method
bc4b2d7: 2013-06-12 Max Medvedev - IDEA-112878 Groovy code completion doesn't obey 'before parentheses -> method parentheses' settings.
1afc036: 2013-09-07 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
80cd221: 2013-09-07 Aleksey Pivovarov - Github: simplify
80a15fc: 2013-09-07 Aleksey Pivovarov - Github: simplify mapping user -> fork repository
ef91648: 2013-09-07 Aleksey Pivovarov - Github: fix remote branch ref
307f921: 2013-09-07 Bas Leijdekkers - IDEA-53184 (False positive unused field inspection for serialVersionUID fields)
c1545b5: 2013-09-06 Vassiliy Kudryashov - Lense mode "internal" preview
5a2c378: 2013-09-06 Roman Shevchenko - platform: correct formatting in downloadable library version chooser
4ffa7ea: 2013-09-06 Konstantin Kolosovsky - IDEA-112802 Fixed parameters for "checkout completed" event
e8a4550: 2013-09-06 peter - there are no constructor field initializers in compiled psi
cee022e: 2013-09-06 peter - IDEA-113013 When insert selected variant is enabled the "generated" code comply with code style
762a0ca: 2013-09-06 peter - empty for loop condition means "true" (IDEA-113242)
75c1cd6: 2013-09-06 peter - IDEA-113149 Constructor parameters completion is not available in case of classes with generics
243adf0: 2013-09-06 peter - IDEA-113028 Complete statement doesn't complete class declaration
9dfa286: 2013-09-06 Anna Kozlova - Merge remote-tracking branch 'origin/master'
d901cc2: 2013-09-06 Anna Kozlova - drop old external annotations format
a0d17e6: 2013-09-06 nik - xdebugger: restored custom presentation for keywords and numbers, changed values are highlighted by coloring their names
65dc74c: 2013-09-06 Sergey Evdokimov - Refactoring: use PsiImplUtil.isLeafElementOfType() instead of org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isLeafElementOfType()
5d1dddc: 2013-09-06 peter - fail-fast DfaMemoryStateImpl.equals
59734e3: 2013-09-06 peter - calculate constructor initialization for all fields at once, and cache (needed for dfa and nullable-stuff inspections)
2bad29c: 2013-09-06 peter - 2x faster implicit field nullability detection
1952eee: 2013-09-06 peter - IDEA-112567 Don't show any completion variants after method reference and dot
6b3d77c: 2013-09-06 peter - IDEA-112968 Show "Value is always" warning only for null/true/false
ae94260: 2013-09-06 peter - revert meaningless: use actual (maybe unsaved) psi text for find usages (EA-49575)
4275fcb: 2013-09-06 Anna Kozlova - drop old external annotations format
d34fcc5: 2013-09-06 Anna Kozlova - move: take into account usages which won't be changed textually but could affect possible conflicts (IDEA-113280)
f1b0f11: 2013-09-06 Sergey Simonchik - xdebug api improvement
1a1eacf: 2013-09-06 Konstantin Kolosovsky - IDEA-112802 Initial checkout implementation (with svn command line client)
b7e7f12: 2013-09-06 Sergey Evdokimov - Bug fix: hint "(, )" is shown beside any goal in maven tool window
953771d: 2013-09-06 Sergey Evdokimov - Run completion automatically after inserting of <dependency>
14e7fec: 2013-09-06 Nadya Zabrodina - fix for windows (template need not to be quoted because GeneralCommandline is smart)
f1a9f68: 2013-09-06 Bas Leijdekkers - IDEA-89557 (Inspection `RawUseOfParameterizedType` is redundant on inspection of parameterized type object arrays with the trailing `class` keyword)
80de1ec: 2013-09-06 Bas Leijdekkers - clarify that the "Raw use of parameterized type" inspection mirrors javac's rawtypes warning (IDEA-99892)
6942d1c: 2013-09-06 Bas Leijdekkers - IDEA-99829 (Problem with inspection "Raw use of parameterized class" option "Ignore where a type parameter would not compile.)
b5c1f0e: 2013-09-06 nik - set default dependency scope for framework libraries added to a module
9683169: 2013-09-06 Eugene Zhuravlev - not-null + manager-thread assertions
86f4be9: 2013-09-06 Anna Kozlova - plugins: do not warn about disabled optional dependencies
fb5e6e7: 2013-09-06 Anna Kozlova - plugins downloading: enable dependencies: make suggester answer Yes/No
ac38433: 2013-09-06 Anna Kozlova - optimization: retrieve PsiElement when problem occurs only
9cd0458: 2013-09-06 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
683ece5: 2013-09-06 nik - xdebugger: 'jump to type source' action added
1448c7d: 2013-09-06 nik - cleanup
4c471d2: 2013-09-06 Vladislav.Soroka - IDEA-102653 tooltip with description added to external project task tree
499cd73: 2013-09-06 Bas Leijdekkers - no generic array creation
6a2d6f4: 2013-09-06 Konstantin Bulenkov - the fix of IDEA-26026 doesn't make sense nowadays. Dialog should be modal IDEA-105611 Invalid focus when invoking find in files
13d56b2: 2013-09-06 Konstantin Kolosovsky - IDEA-94942 Support nullable handler for update commands
9d07f2e: 2013-09-06 Konstantin Kolosovsky - IDEA-94942 Refactored merge, update implementation to use common command listener
8bd8e93: 2013-09-06 Denis Fokin - IDEA-113303 Headless Exception when using inspect.sh on OS X
599c49b: 2013-09-06 Vladimir Krivosheev - init WEB-1171 javascript live console
04b3c6b: 2013-09-06 Konstantin Kolosovsky - IDEA-94942 Force SVNKit implementation for 1.6 working copies
bb8daac: 2013-09-06 Vladimir Krivosheev - investigate: java.lang.IllegalArgumentException: void return types for handler methods not supported
f9f74a5: 2013-09-06 Vladimir Krivosheev - don't hide original exception: java.lang.Exception: java.lang.IllegalStateException: AES/CBC/NoPadding is not available
8df26d7: 2013-09-06 Mikhail Golubev - Merge remote-tracking branch 'origin/master'
87b1100: 2013-09-06 nik - xdebugger api: support grouping values in debugger trees
c1e214a: 2013-09-06 Vladislav.Soroka - IDEA-50450 Gradle code insight
f63250d: 2013-09-04 Alex Ruiz - Disabled "Use embedded Gradle" option for importing project.
517c614: 2013-09-06 Konstantin Bulenkov - remove refs to Project asap
636ac6d: 2013-09-06 Konstantin Bulenkov - IDEA-99897 When "block caret" mode is on, after deleting a character at the end of a line, the character deleted appears in inverse
021d4a5: 2013-06-12 Max Medvedev - Groovy formatter: blank lines in declarations
706feca: 2013-06-12 Max Medvedev - IDEA-113124 Groovy files in the navigation bar
81ba9a5: 2013-09-05 Vladimir Krivosheev - cleanup
198c147: 2013-06-12 Max Medvedev - IDEA-107520 Groovy Shell: support commands in groovy shell
cd1aa72: 2013-06-12 Max Medvedev - IDEA-69673 Generate toString() template Groovy GString style should be added
5e1454a: 2013-06-12 Max Medvedev - generateToString: shorten class references not only for java files
2509702: 2013-06-12 Max Medvedev - don't manually replace toString() if it exists; generate the new one in the appropriate place instead
2e72f08: 2013-06-09 Max Medvedev - Groovy: smart complete type definition body
c8ab923: 2013-06-09 Max Medvedev - Groovy: In-place introduce parameter: delegate
98ae7ed: 2013-09-04 Bas Leijdekkers - Fix all known "Field may be final" inspection problems 2 (IDEA-75321, IDEA-80439, IDEA-84561, IDEA-84614, IDEA-87373,IDEA-89711, IDEA-91969, IDEA-111396)
24184340: 2013-09-05 peter - update FixAllQuickfixTest test data, so that it identifies the quick fix unambiguously
082a0f9: 2013-09-05 Konstantin Bulenkov - make search everywhere available in current context
4eaae16: 2013-09-05 Konstantin Bulenkov - remove dead code
dde304f: 2013-09-05 Konstantin Bulenkov - refactor to make autoSelectOnMouseMove available in ListUtil
150d337: 2013-09-05 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
1790f99: 2013-09-05 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
981c3f6: 2013-09-05 Sergey Evdokimov - Rename class
06eb88d: 2013-09-05 Sergey Evdokimov - Add test for inserting maven dependency by smart completion
dc54349: 2013-09-05 Konstantin Kolosovsky - Merge branch 'svn1_8_new'
a742e91: 2013-09-05 Dmitry Trofimov - Take correct setting for console background color (PY-10737).
c55ba74: 2013-09-05 Nadya Zabrodina - ShellCommandLine changed to GeneralCommandline to provide shell variables to ide
bb3ff5f: 2013-09-05 Sergey Evdokimov - IDEA-112939 Smart dependency completion for pom.xml
d1daa65: 2013-09-05 Anna Kozlova - fix ranges
5ac8d00: 2013-09-05 Anna Kozlova - accept narrowing conversion from byte[] to T< extends Serializable> (IDEA-113225)
5e7db4f: 2013-09-05 Konstantin Kolosovsky - IDEA-112060 Notification added when working copy format and client version do not correspond to each other
804db7e: 2013-09-05 Konstantin Kolosovsky - IDEA-112060 Force validate executable if project is svn 1.8 working copy
cdc6f6b: 2013-09-05 Konstantin Kolosovsky - IDEA-112071 Refresh svn working copies/statuses if "use command line" option or path to executable changed
613144f: 2013-09-05 Konstantin Kolosovsky - Make equal SvnConfigurable and SvnVcs display names (to "show settings" work correctly)
002eadb: 2013-09-05 Serega Vasiliev - javaee libs,project structure, etc.
559ab96: 2013-09-05 peter - no matter if there are other variables having that constant value, it's still constant
bec4757: 2013-09-04 Mikhail Golubev - * Add error message when template variable used isn't defined * Template variables values are urlencoded during their substitution in URLs
c01b85b: 2013-09-04 Mikhail Golubev - Add sprint.ly integration
68d131b: 2013-09-05 peter - use actual (maybe unsaved) psi text for find usages (EA-49575)
b138d05: 2013-09-05 peter - rely less on mutable state in ChooseByNameBase.getNames (IDEA-112779)
f0dc4b1: 2013-09-05 Anna Kozlova - optimization: skip external usages processing when scope already contains everything
bfb94e0: 2013-09-05 Alexey Kudravtsev - renamed module editor-ui-impl to editor-ui-ex
d711697: 2013-09-05 peter - really focus existing project frame when opening already opened project (related to IDEA-113158)
57d5690: 2013-09-05 Anna Kozlova - logging for EA-49553 - assert: SingleInspectionProfilePanel.copyUsedSeveritiesIfUndefined
ed2cc9b: 2013-09-05 Anna Kozlova - logging for EA-49541 - assert: FileReference.rename
5a3cc7c: 2013-09-05 Roman Shevchenko - test framework: commit or dispose model
0735047: 2013-09-05 Anna Kozlova - scopes: escape dot as it is not a separator in file pattern (IDEA-113148)
6ae3bf7: 2013-09-05 Anna Kozlova - inference graph nodes: merge value/dependencies
f02d237: 2013-09-05 Konstantin Bulenkov - fix test data
830b9c1: 2013-09-05 Konstantin Bulenkov - better english
400062d: 2013-09-05 Sergey Evdokimov - Make GrailsRunConfiguration singleton by default.
8caf8af: 2013-09-05 Vladimir Krivosheev - add javadoc
32394f0: 2013-09-05 Vladimir Krivosheev - WEB-7511 "Evaluate Expression" window does not build correct path to currently selected object node
d59afd6: 2013-09-05 nik - xdebugger: removed duplicated 'Collecting data' text value node
32d3038: 2013-09-05 nik - unused class deleted
42255de: 2013-09-05 nik - xdebugger api streamlined: reworked the way node presentation is customized
36d908c: 2013-09-05 Sergey Evdokimov - IDEA-113188 Go to related files works incorrectly for Model and Controller in Groovy
55a5b28: 2013-09-05 Dmitry Avdeev - final methods in task api
cd0f640: 2013-09-04 Dmitry Avdeev - running tests without ModuleTypeManager
a41a8dc: 2013-09-05 Svetlana.Zemlyanskaya - Arrangement: make tokens aware of their type (author=Roman.Shein)
20bfe63: 2013-06-09 Max Medvedev - IDEA-71176 Encapsulate Java fields should update Groovy usages
30889b4: 2013-06-08 Max Medvedev - pluggable EncapsulateFields refactoring
a3ed6bd: 2013-06-08 Max Medvedev - refactor EncapsulateFieldProcessor
b7103d5: 2013-09-04 Max Medvedev - IDEA-113176 Groovy: place caret inside parentheses when compliting constructors
b44e415: 2013-09-04 Vladimir Krivosheev - NotNull, StreamProvider.listSubFiles accept roaming type
89f6078: 2013-09-04 Vladimir Krivosheev - init WEB-7511 "Evaluate Expression" window does not build correct path to currently selected object node Now only hint show correct evaluated expression
faaea11: 2013-09-04 Vladimir Krivosheev - remove deprecated method
98d4f5e: 2013-09-04 Vladimir Krivosheev - cleanup, remove deprecated method
f4a27e8: 2013-09-04 peter - CachedValueProvider.Result: force non-empty list of not-null dependencies
19df6f4: 2013-09-04 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
b12d474: 2013-09-04 Andrey Vlasovskikh - Merge branch 'python-fixes'
824377b: 2013-09-04 Andrey Vlasovskikh - Use new serializer for Python shadowing inspection
9bc2733: 2013-09-04 peter - single-pass control flow building
257d23a: 2013-09-04 peter - IDEA-112474 Expression might evaluate to null and not @Nullable annotated inspection is not aware of @Contract
e586777: 2013-09-04 peter - a more descriptive DupInstruction.toString
9c9e366: 2013-09-04 peter - dfa: pop all the contract-unchecked arguments from stack (IDEA-112500)
6c2ead1: 2013-09-04 jgindin - Branch view in RepositoryTab
616f50f: 2013-09-04 Konstantin Kolosovsky - Merge branch 'svn1_8_new'
631a6ae: 2013-09-04 Konstantin Bulenkov - IDEA-113217 Check spelling and grammar in Community Bundles
45009eb: 2013-09-04 Konstantin Bulenkov - IDEA-113216 Darcula: Equal sign is too bright in properties files
711b2f5: 2013-09-04 Konstantin Kolosovsky - IDEA-94942 Force separate SVNKit client for each info request (to prevent multithreaded issues)
9ea7a68: 2013-09-04 Konstantin Bulenkov - IDEA-113215 GSP looks weird under Darcula
76f2e32: 2013-09-04 Konstantin Kolosovsky - IDEA-94942 Removed unused JavaHL code
3a8a697: 2013-09-04 Vladimir Krivosheev - fix possible NPE
1dd1635: 2013-09-04 Vladimir Krivosheev - We must not use variable if it is not yet set — we must set channel in "init" handler, but not in "connected" handler. Issue is actual under windows (I don't know why).
4860142: 2013-09-04 Mikhail Golubev - Change TaskBuilder constructor: repository field is mandatory
ccb5502: 2013-09-04 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
d43513f: 2013-09-03 Dmitry Trofimov - Variable view in Python Console.
dd251c7: 2013-09-03 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
dc83844: 2013-09-03 Dmitry Trofimov - Prevent CCE.
Change-Id: I9ce8f336e5ab08b9a26d806a3faa8db7e0d9813f
Diffstat (limited to 'plugins/InspectionGadgets/InspectionGadgetsAnalysis')
11 files changed, 1202 insertions, 746 deletions
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index ed346b24623b..90800e45d34c 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1729,7 +1729,7 @@ constant.value.variable.use.problem.descriptor=Value of <code>#ref</code> is kno replace.reference.with.expression.quickfix=Replace with ''{0}'' unnecessary.parentheses.option=Ignore clarifying parentheses unnecessary.parentheses.conditional.option=Ignore parentheses around the condition of conditional expressions -field.may.be.final.display.name=Field may be final +field.may.be.final.display.name=Field may be 'final' field.may.be.final.problem.descriptor=Field <code>#ref</code> may be 'final' #loc cast.that.loses.precision.option=Ignore casts from int to char variable.not.used.inside.if.display.name=Reference checked for 'null' is not used inside 'if' diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/assignment/IncrementDecrementUsedAsExpressionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/assignment/IncrementDecrementUsedAsExpressionInspection.java index cc40f05645e8..bcebcd9cd9ef 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/assignment/IncrementDecrementUsedAsExpressionInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/assignment/IncrementDecrementUsedAsExpressionInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 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. @@ -23,7 +23,6 @@ import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.tree.IElementType; 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; @@ -85,6 +84,9 @@ public class IncrementDecrementUsedAsExpressionInspection @Nullable protected InspectionGadgetsFix buildFix(Object... infos) { final PsiExpression expression = (PsiExpression)infos[0]; + if (PsiTreeUtil.getParentOfType(expression, PsiCodeBlock.class, true, PsiMember.class) == null) { + return null; + } return new IncrementDecrementUsedAsExpressionFix(expression.getText()); } @@ -112,8 +114,7 @@ public class IncrementDecrementUsedAsExpressionInspection } @Override - protected void doFix(Project project, ProblemDescriptor descriptor) - throws IncorrectOperationException { + protected void doFix(Project project, ProblemDescriptor descriptor) { // see also the Extract Increment intention of IPP final PsiElement element = descriptor.getPsiElement(); final PsiExpression operand; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassInspection.java index dda5dac769f1..9992f57f39dd 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2012 Bas Leijdekkers + * Copyright 2006-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. @@ -15,11 +15,13 @@ */ package com.siyeh.ig.inheritance; +import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Query; import com.siyeh.InspectionGadgetsBundle; @@ -127,10 +129,7 @@ public class TypeParameterExtendsFinalClassInspection extends BaseInspection { } final PsiClassType extendsType = extendsListTypes[0]; final PsiClass aClass = extendsType.resolve(); - if (aClass == null) { - return; - } - if (!aClass.hasModifierProperty(PsiModifier.FINAL)) { + if (aClass == null || !aClass.hasModifierProperty(PsiModifier.FINAL)) { return; } final PsiIdentifier nameIdentifier = classParameter.getNameIdentifier(); @@ -156,22 +155,63 @@ public class TypeParameterExtendsFinalClassInspection extends BaseInspection { if (aClass == null || !aClass.hasModifierProperty(PsiModifier.FINAL)) { return; } - if (isPartOfOverriddenMethod(typeElement)) { + if (!shouldReport(typeElement)) { return; } registerError(typeElement.getFirstChild(), aClass, Integer.valueOf(2)); } - private static boolean isPartOfOverriddenMethod(PsiTypeElement typeElement) { - final PsiMethod method = PsiTreeUtil.getParentOfType(typeElement, PsiMethod.class); - if (method == null) { - return false; + private static boolean shouldReport(PsiTypeElement typeElement) { + final PsiElement ancestor = PsiTreeUtil.skipParentsOfType( + typeElement, PsiTypeElement.class, PsiJavaCodeReferenceElement.class, PsiReferenceParameterList.class); + if (ancestor instanceof PsiParameter) { + final PsiParameter parameter = (PsiParameter)ancestor; + final PsiElement scope = parameter.getDeclarationScope(); + if (scope instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)scope; + if (MethodUtils.hasSuper(method)) { + return false; + } + } + else if (scope instanceof PsiForeachStatement) { + final PsiForeachStatement foreachStatement = (PsiForeachStatement)scope; + final PsiParameter iterationParameter = foreachStatement.getIterationParameter(); + final PsiType iterationType = iterationParameter.getType(); + final PsiExpression iteratedValue = foreachStatement.getIteratedValue(); + if (iteratedValue == null) { + return false; // incomplete code + } + final PsiType type = JavaGenericsUtil.getCollectionItemType(iteratedValue); + if (type == null || !TypeConversionUtil.isAssignable(iterationType, type)) { // sanity check + return false; + } + if (type.equals(iterationType)) { + return false; + } + if (!(type instanceof PsiCapturedWildcardType)) { + return true; + } + final PsiCapturedWildcardType capturedWildcardType = (PsiCapturedWildcardType)type; + final PsiType upperBound = capturedWildcardType.getUpperBound(); + if (iterationType.equals(upperBound)) { + return false; + } + } } - final PsiCodeBlock body = method.getBody(); - if (PsiTreeUtil.isAncestor(body, typeElement, true)) { - return false; + else if (ancestor instanceof PsiLocalVariable) { + final PsiLocalVariable localVariable = (PsiLocalVariable)ancestor; + final PsiExpression initializer = localVariable.getInitializer(); + if (initializer == null) { + return true; + } + final PsiType type = initializer.getType(); + final PsiType expectedType = GenericsUtil.getVariableTypeByExpressionType(type); + final PsiType variableType = localVariable.getType(); + if (variableType.equals(expectedType)) { + return false; + } } - return MethodUtils.hasSuper(method); + return true; } } }
\ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/MethodMayBeStaticInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/MethodMayBeStaticInspection.java index 57ac5aebf88d..51c7c511d6c8 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/MethodMayBeStaticInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/MethodMayBeStaticInspection.java @@ -29,7 +29,7 @@ import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.ChangeModifierFix; import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.MethodUtils; -import org.jetbrains.annotations.NonNls; +import com.siyeh.ig.psiutils.SerializationUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -168,57 +168,8 @@ public class MethodMayBeStaticInspection extends BaseInspection { } private boolean isExcluded(PsiMethod method) { - @NonNls final String name = method.getName(); - if ("writeObject".equals(name)) { - if (!method.hasModifierProperty(PsiModifier.PRIVATE)) { - return false; - } - if (!MethodUtils.hasInThrows(method, "java.io.IOException")) { - 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 parameter = parameterList.getParameters()[0]; - final PsiType type = parameter.getType(); - return type.equalsToText("java.io.ObjectOutputStream"); - } - if ("readObject".equals(name)) { - if (!method.hasModifierProperty(PsiModifier.PRIVATE)) { - return false; - } - if (!MethodUtils.hasInThrows(method, "java.io.IOException", "java.lang.ClassNotFoundException")) { - 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 parameter = parameterList.getParameters()[0]; - final PsiType type = parameter.getType(); - return type.equalsToText("java.io.ObjectInputStream"); - } - if ("writeReplace".equals(name) || "readResolve".equals(name)) { - if (!MethodUtils.hasInThrows(method, "java.io.ObjectStreamException")) { - return false; - } - final PsiType returnType = method.getReturnType(); - if (returnType == null || !returnType.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) { - return false; - } - final PsiParameterList parameterList = method.getParameterList(); - return parameterList.getParametersCount() == 0; - } - return false; + return SerializationUtils.isWriteObject(method) || SerializationUtils.isReadObject(method) || + SerializationUtils.isWriteReplace(method) || SerializationUtils.isReadResolve(method); } } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignment.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignment.java new file mode 100644 index 000000000000..893de087fa06 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignment.java @@ -0,0 +1,148 @@ +/* + * 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. + */ + +/** + * (c) 2013 Desert Island BV + * created: 03 09 2013 + */ +package com.siyeh.ig.psiutils; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** +* @author Bas Leijdekkers +*/ +public class DefiniteAssignment { + + @NotNull + private final PsiVariable variable; + private final Map<PsiElement, Boolean> definitelyAssignedBeforeBreak = new HashMap(); + private final Map<PsiElement, Boolean> definitelyUnassignedBeforeBreak = new HashMap(); + private final Map<PsiElement, Boolean> definitelyAssignedBeforeContinue = new HashMap(); + private final Map<PsiElement, Boolean> definitelyUnassignedBeforeContinue = new HashMap(); + private final Map<PsiElement, Boolean> definitelyAssignedBeforeReturn = new HashMap(); + private final Map<PsiElement, Boolean> definitelyUnassignedBeforeReturn = new HashMap(); + private boolean definitelyAssigned = false; + private boolean definitelyUnassigned = true; + + public DefiniteAssignment(@NotNull PsiVariable variable) { + this.variable = variable; + } + + public void and(boolean definitelyAssigned, boolean definitelyUnassigned) { + this.definitelyAssigned &= definitelyAssigned; + this.definitelyUnassigned &= definitelyUnassigned; + } + + public void assign(@NotNull PsiReferenceExpression expression, boolean definiteAssignment) { + if (definiteAssignment) { + definitelyAssigned = true; + } + else { + valueAccess(expression); + } + definitelyUnassigned = false; + } + + public void andDefiniteAssignmentBeforeBreak(PsiStatement statement) { + definitelyAssigned &= removeValue(statement, definitelyAssignedBeforeBreak); + definitelyUnassigned &= removeValue(statement, definitelyUnassignedBeforeBreak); + } + + public void andDefiniteAssignmentBeforeContinue(PsiStatement statement) { + definitelyAssigned &= removeValue(statement, definitelyAssignedBeforeContinue); + definitelyUnassigned &= removeValue(statement, definitelyUnassignedBeforeContinue); + } + + public void andDefiniteAssignmentBeforeReturn(PsiMethod method) { + definitelyAssigned &= removeValue(method, definitelyAssignedBeforeReturn); + definitelyUnassigned &= removeValue(method, definitelyUnassignedBeforeReturn); + } + + private static boolean removeValue(PsiElement statement, Map<PsiElement, Boolean> map) { + final Boolean aBoolean = map.remove(statement); + return aBoolean == null || aBoolean.booleanValue(); + } + + @NotNull + public final PsiVariable getVariable() { + return variable; + } + + public boolean isDefinitelyAssigned() { + return definitelyAssigned; + } + + public boolean isDefinitelyUnassigned() { + return definitelyUnassigned; + } + + public void set(boolean definitelyAssigned, boolean definitelyUnassigned) { + this.definitelyAssigned = definitelyAssigned; + this.definitelyUnassigned = definitelyUnassigned; + } + + public boolean stop() { + return false; + } + + public void storeBeforeBreakStatement(PsiBreakStatement breakStatement) { + final PsiStatement statement = breakStatement.findExitedStatement(); + if (statement == null) { + return; + } + storeFor(statement, definitelyAssignedBeforeBreak, definitelyUnassignedBeforeBreak); + } + + public void storeBeforeContinueStatement(PsiContinueStatement continueStatement) { + final PsiStatement statement = continueStatement.findContinuedStatement(); + if (statement == null) { + return; + } + storeFor(statement, definitelyAssignedBeforeContinue, definitelyUnassignedBeforeContinue); + } + + public void storeBeforeReturn(PsiReturnStatement returnStatement) { + final PsiMethod method = PsiTreeUtil.getParentOfType(returnStatement, PsiMethod.class, true, PsiClass.class); + if (method == null || !method.isConstructor()) { + return; + } + storeFor(method, definitelyAssignedBeforeReturn, definitelyUnassignedBeforeReturn); + } + + private void storeFor(PsiElement element, + Map<PsiElement, Boolean> definitelyAssignedMap, + Map<PsiElement, Boolean> definitelyUnassignedMap) { + final Boolean existingDa = definitelyAssignedMap.get(element); + final Boolean existingDu = definitelyUnassignedMap.get(element); + definitelyAssignedMap.put(element, Boolean.valueOf(definitelyAssigned && (existingDa == null || existingDa.booleanValue()))); + definitelyUnassignedMap.put(element, Boolean.valueOf(definitelyUnassigned && (existingDu == null || existingDu.booleanValue()))); + } + + @Override @NonNls + public String toString() { + return "DefiniteAssignment{ variable=" + variable + ", definitelyAssigned=" + definitelyAssigned + + ", definitelyUnassigned=" + definitelyUnassigned + '}'; + } + + public void valueAccess(PsiReferenceExpression expression) {} +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignmentUtil.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignmentUtil.java new file mode 100644 index 000000000000..63bb1bb87f57 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/DefiniteAssignmentUtil.java @@ -0,0 +1,792 @@ +/* + * 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.psiutils; + +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Implementation of Java Language Specification, Chapter 16. Definite Assignment + * with some changes to match javac behavior where it does not match the specification. + * @author Bas Leijdekkers + */ +public final class DefiniteAssignmentUtil { + + public static void checkVariable(PsiVariable variable, DefiniteAssignment definiteAssignment) { + if (variable instanceof PsiField) { + final PsiField field = (PsiField)variable; + checkField(field, definiteAssignment); + } + } + + private static void checkField(PsiField field, DefiniteAssignment definiteAssignment) { + if (field.getInitializer() != null) { + definiteAssignment.set(true, false); + } + final PsiClass aClass = field.getContainingClass(); + if (aClass == null) { + return; + } + final PsiElement[] children = aClass.getChildren(); + if (field.hasModifierProperty(PsiModifier.STATIC)) { + for (PsiElement child : children) { + if (child instanceof PsiField) { + final PsiField otherField = (PsiField)child; + if (!otherField.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + checkExpression(otherField.getInitializer(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + else if (child instanceof PsiClassInitializer) { + final PsiClassInitializer classInitializer = (PsiClassInitializer)child; + if (!classInitializer.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + checkCodeBlock(classInitializer.getBody(), definiteAssignment); + } + } + } + else { + for (PsiElement child : children) { + if (child instanceof PsiField) { + final PsiField otherField = (PsiField)child; + if (otherField.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + checkExpression(otherField.getInitializer(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + else if (child instanceof PsiClassInitializer) { + final PsiClassInitializer classInitializer = (PsiClassInitializer)child; + if (classInitializer.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + checkCodeBlock(classInitializer.getBody(), definiteAssignment); + } + if (definiteAssignment.stop()) return; + } + final PsiMethod[] constructors = aClass.getConstructors(); + if (constructors.length != 0) { // missing from spec? + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + boolean resultDa = true; + boolean resultDu = true; + for (PsiMethod constructor : constructors) { + checkConstructor(constructor, definiteAssignment); + resultDa &= definiteAssignment.isDefinitelyAssigned(); + resultDu &= definiteAssignment.isDefinitelyUnassigned(); + if (definiteAssignment.stop()) return; + definiteAssignment.set(da, du); + } + definiteAssignment.set(resultDa, resultDu); + } + } + } + + private static boolean isAlternateConstructorInvocation(PsiStatement statement) { + return isConstructorInvocation(statement, PsiKeyword.THIS); + } + + private static boolean isSuperClassConstructorInvocation(PsiStatement statement) { + return isConstructorInvocation(statement, PsiKeyword.SUPER); + } + + private static boolean isConstructorInvocation(PsiStatement statement, @NotNull String keyword) { + 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(); + return keyword.equals(methodExpression.getReferenceName()); + } + + private static void checkAnonymousClass(PsiAnonymousClass anonymousClass, DefiniteAssignment definiteAssignment) { + for (PsiField field : anonymousClass.getFields()) { + checkExpression(field.getInitializer(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + final PsiClassInitializer[] initializers = anonymousClass.getInitializers(); + for (PsiClassInitializer initializer : initializers) { + checkCodeBlock(initializer.getBody(), definiteAssignment); + } + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(true, false); + for (PsiMethod method : anonymousClass.getMethods()) { + checkCodeBlock(method.getBody(), definiteAssignment); + } + definiteAssignment.set(da, du); + } + + private static void checkClass(PsiClass aClass, DefiniteAssignment definiteAssignment) { + for (PsiField field : aClass.getFields()) { + checkExpression(field.getInitializer(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + for (PsiClassInitializer initializer : aClass.getInitializers()) { + checkCodeBlock(initializer.getBody(), definiteAssignment); + } + for (PsiMethod method : aClass.getMethods()) { + checkCodeBlock(method.getBody(), definiteAssignment); + } + for (PsiClass innerClass : aClass.getInnerClasses()) { + checkClass(innerClass, definiteAssignment); + } + } + + private static void checkConstructor(PsiMethod constructor, DefiniteAssignment definiteAssignment) { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(false, true); + final PsiCodeBlock body = constructor.getBody(); + if (body == null) { + return; + } + final PsiStatement[] statements = body.getStatements(); + boolean superCalled = false; + for (int i = 0; i < statements.length; i++) { + final PsiStatement statement = statements[i]; + if (i == 0) { + if (isAlternateConstructorInvocation(statement)) { + checkStatement(statement, definiteAssignment); + definiteAssignment.set(true, false); + superCalled = true; + continue; + } + else if (isSuperClassConstructorInvocation(statement)) { + checkStatement(statement, definiteAssignment); + definiteAssignment.set(da, du); + superCalled = true; + continue; + } + } + if (!superCalled) { + // implicit super call + definiteAssignment.set(da, du); + superCalled = true; + } + checkStatement(statement,definiteAssignment); + } + if (!superCalled) { + // constructor has no statements + definiteAssignment.set(da, du); + } + definiteAssignment.andDefiniteAssignmentBeforeReturn(constructor); // missing from spec? + } + + private static void checkCodeBlock(@Nullable PsiCodeBlock codeBlock, DefiniteAssignment definiteAssignment) { + if (codeBlock == null) { + return; + } + for (PsiStatement statement : codeBlock.getStatements()) { + checkStatement(statement, definiteAssignment); + } + } + + private static void checkLocalVariable(PsiLocalVariable localVariable, DefiniteAssignment definiteAssignment) { + final PsiExpression initializer = localVariable.getInitializer(); + if (initializer != null && definiteAssignment.getVariable() == localVariable) { + definiteAssignment.set(true, false); + } + checkExpression(initializer, definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + + private static void checkStatement(@Nullable PsiStatement statement, DefiniteAssignment definiteAssignment) { + if (statement == null || definiteAssignment.stop() || statement instanceof PsiEmptyStatement) { + return; + } + if (statement instanceof PsiAssertStatement) { + final PsiAssertStatement assertStatement = (PsiAssertStatement)statement; + checkAssertStatement(assertStatement, definiteAssignment); + } + else if (statement instanceof PsiBlockStatement) { + final PsiBlockStatement blockStatement = (PsiBlockStatement)statement; + checkCodeBlock(blockStatement.getCodeBlock(), definiteAssignment); + } + else if (statement instanceof PsiBreakStatement) { + final PsiBreakStatement breakStatement = (PsiBreakStatement)statement; + checkBreakStatement(breakStatement, definiteAssignment); + } + else if (statement instanceof PsiContinueStatement) { + final PsiContinueStatement continueStatement = (PsiContinueStatement)statement; + checkContinueStatement(continueStatement, definiteAssignment); + } + else if (statement instanceof PsiDeclarationStatement) { + final PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement)statement; + checkDeclarationStatement(declarationStatement, definiteAssignment); + } + else if (statement instanceof PsiDoWhileStatement) { + final PsiDoWhileStatement doWhileStatement = (PsiDoWhileStatement)statement; + checkDoWhileStatement(doWhileStatement, definiteAssignment); + } + else if (statement instanceof PsiExpressionStatement) { + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)statement; + checkExpression(expressionStatement.getExpression(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + else if (statement instanceof PsiExpressionListStatement) { + final PsiExpressionListStatement expressionListStatement = (PsiExpressionListStatement)statement; + checkExpressionListStatement(expressionListStatement, definiteAssignment); + } + else if (statement instanceof PsiForeachStatement) { + final PsiForeachStatement foreachStatement = (PsiForeachStatement)statement; + checkForeachStatement(foreachStatement, definiteAssignment); + } + else if (statement instanceof PsiForStatement) { + final PsiForStatement forStatement = (PsiForStatement)statement; + checkForStatement(forStatement, definiteAssignment); + } + else if (statement instanceof PsiIfStatement) { + final PsiIfStatement ifStatement = (PsiIfStatement)statement; + checkIfStatement(ifStatement, definiteAssignment); + } + else if (statement instanceof PsiLabeledStatement) { + final PsiLabeledStatement labeledStatement = (PsiLabeledStatement)statement; + checkLabeledStatement(labeledStatement, definiteAssignment); + } + else if (statement instanceof PsiReturnStatement) { + final PsiReturnStatement returnStatement = (PsiReturnStatement)statement; + checkReturnStatement(returnStatement, definiteAssignment); + } + else if (statement instanceof PsiSwitchStatement) { + final PsiSwitchStatement switchStatement = (PsiSwitchStatement)statement; + checkSwitchStatement(switchStatement, definiteAssignment); + } + else if (statement instanceof PsiSynchronizedStatement) { + final PsiSynchronizedStatement synchronizedStatement = (PsiSynchronizedStatement)statement; + checkSynchronizedStatement(synchronizedStatement, definiteAssignment); + } + else if (statement instanceof PsiThrowStatement) { + final PsiThrowStatement throwStatement = (PsiThrowStatement)statement; + checkThrowStatement(throwStatement, definiteAssignment); + } + else if (statement instanceof PsiTryStatement) { + final PsiTryStatement tryStatement = (PsiTryStatement)statement; + checkTryStatement(tryStatement, definiteAssignment); + } + else if (statement instanceof PsiWhileStatement) { + final PsiWhileStatement whileStatement = (PsiWhileStatement)statement; + checkWhileStatement(whileStatement, definiteAssignment); + } + } + + private static void checkAssertStatement(PsiAssertStatement assertStatement, DefiniteAssignment definiteAssignment) { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + final PsiExpression condition = assertStatement.getAssertCondition(); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + final boolean resultDu = du && definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(da, du); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + checkExpression(assertStatement.getAssertDescription(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + definiteAssignment.set(da, resultDu); + } + + private static void checkBreakStatement(PsiBreakStatement breakStatement, DefiniteAssignment definiteAssignment) { + definiteAssignment.storeBeforeBreakStatement(breakStatement); + definiteAssignment.set(true, true); + } + + private static void checkContinueStatement(PsiContinueStatement continueStatement, DefiniteAssignment definiteAssignment) { + definiteAssignment.storeBeforeContinueStatement(continueStatement); + definiteAssignment.set(true, true); + } + + private static void checkDeclarationStatement(PsiDeclarationStatement declarationStatement, DefiniteAssignment definiteAssignment) { + for (PsiElement element : declarationStatement.getDeclaredElements()) { + if (element instanceof PsiLocalVariable) { + final PsiLocalVariable variable = (PsiLocalVariable)element; + checkLocalVariable(variable, definiteAssignment); + } + else if (element instanceof PsiClass) { + final PsiClass aClass = (PsiClass)element; + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(true, false); + checkClass(aClass, definiteAssignment); + definiteAssignment.set(da, du); + } + else { + throw new AssertionError("unknown element declared: " + element); + } + } + } + + private static void checkDoWhileStatement(PsiDoWhileStatement doWhileStatement, DefiniteAssignment definiteAssignment) { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + final PsiExpression condition = doWhileStatement.getCondition(); + final PsiStatement body = doWhileStatement.getBody(); + checkStatement(body, definiteAssignment); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + definiteAssignment.set(da, definiteAssignment.isDefinitelyUnassigned() & du); + checkStatement(body, definiteAssignment); + definiteAssignment.andDefiniteAssignmentBeforeContinue(doWhileStatement); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + definiteAssignment.andDefiniteAssignmentBeforeBreak(doWhileStatement); + } + + private static void checkExpressionListStatement(PsiExpressionListStatement expressionListStatement, + DefiniteAssignment definiteAssignment) { + final PsiExpressionList expressionList = expressionListStatement.getExpressionList(); + for (PsiExpression expression : expressionList.getExpressions()) { + checkExpression(expression, definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + } + + private static void checkForeachStatement(PsiForeachStatement foreachStatement, DefiniteAssignment definiteAssignment) { + checkExpression(foreachStatement.getIteratedValue(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + final boolean da = definiteAssignment.isDefinitelyAssigned(); + checkStatement(foreachStatement.getBody(), definiteAssignment); + definiteAssignment.set(da, definiteAssignment.isDefinitelyUnassigned()); + definiteAssignment.andDefiniteAssignmentBeforeBreak(foreachStatement); + } + + private static void checkForStatement(PsiForStatement forStatement, DefiniteAssignment definiteAssignment) { + checkStatement(forStatement.getInitialization(), definiteAssignment); + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + final PsiExpression condition = forStatement.getCondition(); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + checkStatement(forStatement.getBody(), definiteAssignment); + definiteAssignment.andDefiniteAssignmentBeforeContinue(forStatement); + checkStatement(forStatement.getUpdate(), definiteAssignment); + // hack because javac does not match spec for incrementation part of for statement + checkStatement(forStatement.getBody(), definiteAssignment); + definiteAssignment.set(da, du); + if (condition == null) { + definiteAssignment.set(true, true); + } + else { + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + } + definiteAssignment.andDefiniteAssignmentBeforeBreak(forStatement); + } + + private static void checkIfStatement(PsiIfStatement ifStatement, DefiniteAssignment definiteAssignment) { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + final PsiExpression condition = ifStatement.getCondition(); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + checkStatement(ifStatement.getThenBranch(), definiteAssignment); + final boolean resultDa = definiteAssignment.isDefinitelyAssigned(); + final boolean resultDu = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(da, du); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + checkStatement(ifStatement.getElseBranch(), definiteAssignment); + definiteAssignment.and(resultDa, resultDu); + } + + private static void checkLabeledStatement(PsiLabeledStatement labeledStatement, DefiniteAssignment definiteAssignment) { + final PsiStatement statement = labeledStatement.getStatement(); + checkStatement(statement, definiteAssignment); + definiteAssignment.andDefiniteAssignmentBeforeBreak(statement); + } + + private static void checkReturnStatement(PsiReturnStatement returnStatement, DefiniteAssignment definiteAssignment) { + definiteAssignment.storeBeforeReturn(returnStatement); + checkExpression(returnStatement.getReturnValue(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + definiteAssignment.set(true, true); + } + + private static void checkSwitchStatement(PsiSwitchStatement switchStatement, DefiniteAssignment definiteAssignment) { + checkExpression(switchStatement.getExpression(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + final PsiCodeBlock body = switchStatement.getBody(); + if (body == null) { + return; + } + final PsiStatement[] statements = body.getStatements(); + if (statements.length == 0) { + return; + } + boolean defaultSeen = false; + for (final PsiStatement statement : statements) { + if (statement instanceof PsiSwitchLabelStatement) { + final PsiSwitchLabelStatement switchLabelStatement = (PsiSwitchLabelStatement)statement; + if (switchLabelStatement.isDefaultCase()) { + defaultSeen = true; + } + } + checkStatement(statement, definiteAssignment); + } + definiteAssignment.andDefiniteAssignmentBeforeBreak(switchStatement); // enum switch not specified + definiteAssignment.and(defaultSeen, definiteAssignment.isDefinitelyUnassigned()); + } + + private static void checkSynchronizedStatement(PsiSynchronizedStatement synchronizedStatement, DefiniteAssignment definiteAssignment) { + checkExpression(synchronizedStatement.getLockExpression(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + checkCodeBlock(synchronizedStatement.getBody(), definiteAssignment); + } + + private static void checkThrowStatement(PsiThrowStatement throwStatement, DefiniteAssignment definiteAssignment) { + checkExpression(throwStatement.getException(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + definiteAssignment.set(true, true); + } + + private static void checkTryStatement(PsiTryStatement tryStatement, DefiniteAssignment definiteAssignment) { + // try with resources not specified + final boolean da = definiteAssignment.isDefinitelyAssigned(); + checkCodeBlock(tryStatement.getTryBlock(), definiteAssignment); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + boolean resultDa = definiteAssignment.isDefinitelyAssigned(); + boolean resultDu = du; + for (PsiCodeBlock catchBlock : tryStatement.getCatchBlocks()) { + definiteAssignment.set(da, du); + checkCodeBlock(catchBlock, definiteAssignment); + resultDu &= definiteAssignment.isDefinitelyUnassigned(); + resultDa &= definiteAssignment.isDefinitelyAssigned(); + } + final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); + if (finallyBlock != null) { + definiteAssignment.set(da, definiteAssignment.isDefinitelyUnassigned()); + checkCodeBlock(finallyBlock, definiteAssignment); + resultDu &= definiteAssignment.isDefinitelyUnassigned(); // spec problem + } + definiteAssignment.set(resultDa | definiteAssignment.isDefinitelyAssigned(), resultDu); + } + + private static void checkWhileStatement(PsiWhileStatement whileStatement, DefiniteAssignment definiteAssignment) { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final PsiExpression condition = whileStatement.getCondition(); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + checkStatement(whileStatement.getBody(), definiteAssignment); + definiteAssignment.andDefiniteAssignmentBeforeContinue(whileStatement); + definiteAssignment.set(da, definiteAssignment.isDefinitelyUnassigned()); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + definiteAssignment.andDefiniteAssignmentBeforeBreak(whileStatement); + } + + private static void checkExpression(@Nullable PsiExpression expression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + if (expression == null || definiteAssignment.stop()) { + return; + } + if (PsiType.BOOLEAN.equals(expression.getType())) { + final Object result = ExpressionUtils.computeConstantExpression(expression); + if (Boolean.TRUE == result) { + if (BooleanExpressionValue.WHEN_FALSE == value) { + definiteAssignment.set(true, true); + } + return; + } + else if (Boolean.FALSE == result) { + if (BooleanExpressionValue.WHEN_TRUE == value) { + definiteAssignment.set(true, true); + } + return; + } + } + if (expression instanceof PsiArrayAccessExpression) { + final PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression)expression; + checkArrayAccessExpression(arrayAccessExpression, definiteAssignment, value); + } + else if (expression instanceof PsiArrayInitializerExpression) { + final PsiArrayInitializerExpression arrayInitializerExpression = (PsiArrayInitializerExpression)expression; + checkArrayInitializerExpression(arrayInitializerExpression, definiteAssignment, value); + } + else if (expression instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression; + checkAssignmentExpression(assignmentExpression, definiteAssignment); + } + else if (expression instanceof PsiConditionalExpression) { + final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)expression; + checkConditionalExpression(conditionalExpression, definiteAssignment, value); + } + else if (expression instanceof PsiInstanceOfExpression) { + final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression)expression; + checkExpression(instanceOfExpression.getOperand(), definiteAssignment, value); + } + else if (expression instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + checkMethodCallExpression(methodCallExpression, definiteAssignment, value); + } + else if (expression instanceof PsiNewExpression) { + final PsiNewExpression newExpression = (PsiNewExpression)expression; + checkNewExpression(newExpression, definiteAssignment, value); + } + else if (expression instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression; + checkExpression(parenthesizedExpression.getExpression(), definiteAssignment, value); + } + else if (expression instanceof PsiPolyadicExpression) { + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; + checkPolyadicExpression(polyadicExpression, definiteAssignment, value); + } + else if (expression instanceof PsiPostfixExpression) { + final PsiPostfixExpression postfixExpression = (PsiPostfixExpression)expression; + checkPostFixExpression(postfixExpression, definiteAssignment); + } + else if (expression instanceof PsiPrefixExpression) { + final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)expression; + checkPrefixExpression(prefixExpression, definiteAssignment, value); + } + else if (expression instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression; + checkReferenceExpression(referenceExpression, definiteAssignment, value); + } + else if (expression instanceof PsiTypeCastExpression) { + final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)expression; + checkExpression(typeCastExpression.getOperand(), definiteAssignment, value); + } + } + + private static void checkArrayAccessExpression(PsiArrayAccessExpression arrayAccessExpression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + checkExpression(arrayAccessExpression.getArrayExpression(), definiteAssignment, value); + checkExpression(arrayAccessExpression.getIndexExpression(), definiteAssignment, value); + } + + private static void checkArrayInitializerExpression(PsiArrayInitializerExpression arrayInitializerExpression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + for (PsiExpression initializer : arrayInitializerExpression.getInitializers()) { + checkExpression(initializer, definiteAssignment, value); + } + } + + private static void checkAssignmentExpression(PsiAssignmentExpression assignmentExpression, DefiniteAssignment definiteAssignment) { + final PsiExpression lhs = assignmentExpression.getLExpression(); + final PsiReferenceExpression referenceExpression = getReferenceIfAssignmentOfVariable(lhs, definiteAssignment); + if (referenceExpression == null) { + checkExpression(lhs, definiteAssignment, BooleanExpressionValue.UNDEFINED); + checkExpression(assignmentExpression.getRExpression(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + else { + final IElementType tokenType = assignmentExpression.getOperationTokenType(); + if (!JavaTokenType.EQ.equals(tokenType)) { + definiteAssignment.valueAccess(referenceExpression); + } + checkExpression(assignmentExpression.getRExpression(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + definiteAssignment.assign(referenceExpression, true); + } + } + + private static PsiReferenceExpression getReferenceIfAssignmentOfVariable(PsiExpression lhs, DefiniteAssignment definiteAssignment) { + lhs = ParenthesesUtils.stripParentheses(lhs); + if (!(lhs instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; + final PsiElement target = referenceExpression.resolve(); + if (!definiteAssignment.getVariable().equals(target)) { + return null; + } + final PsiExpression qualifier = referenceExpression.getQualifierExpression(); + if (qualifier != null && (!(qualifier instanceof PsiThisExpression) || ((PsiThisExpression)qualifier).getQualifier() != null)) { + return null; + } + return referenceExpression; + } + + private static void checkConditionalExpression(PsiConditionalExpression conditionalExpression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + final PsiExpression thenExpression = conditionalExpression.getThenExpression(); + final PsiExpression elseExpression = conditionalExpression.getElseExpression(); + if (thenExpression != null && PsiType.BOOLEAN.equals(thenExpression.getType()) && + elseExpression != null && PsiType.BOOLEAN.equals(elseExpression.getType())) { + if (value == BooleanExpressionValue.WHEN_TRUE) { + checkConditionalInternal(conditionalExpression, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + } + else if (value == BooleanExpressionValue.WHEN_FALSE) { + checkConditionalInternal(conditionalExpression, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + } + else { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + checkConditionalExpression(conditionalExpression, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + final boolean resultDa = definiteAssignment.isDefinitelyAssigned(); + final boolean resultDu = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(da, du); + checkConditionalExpression(conditionalExpression, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + definiteAssignment.and(resultDa, resultDu); + } + } + else { + checkConditionalInternal(conditionalExpression, definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + } + + private static void checkConditionalInternal(PsiConditionalExpression conditionalExpression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + final PsiExpression condition = conditionalExpression.getCondition(); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + checkExpression(conditionalExpression.getThenExpression(), definiteAssignment, value); + final boolean resultDa = definiteAssignment.isDefinitelyAssigned(); + final boolean resultDu = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(da, du); + checkExpression(condition, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + checkExpression(conditionalExpression.getElseExpression(), definiteAssignment, value); + definiteAssignment.and(resultDa, resultDu); + } + + private static void checkMethodCallExpression(PsiMethodCallExpression methodCallExpression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + checkExpression(qualifier, definiteAssignment, value); + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + for (PsiExpression argument : argumentList.getExpressions()) { + checkExpression(argument, definiteAssignment, value); + } + } + + private static void checkNewExpression(PsiNewExpression newExpression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + final PsiExpressionList argumentList = newExpression.getArgumentList(); + if (argumentList != null) { + for (PsiExpression argument : argumentList.getExpressions()) { + checkExpression(argument, definiteAssignment, value); + } + } + final PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass(); + if (anonymousClass != null) { + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(definiteAssignment.isDefinitelyAssigned(), false); + checkAnonymousClass(anonymousClass, definiteAssignment); + definiteAssignment.set(definiteAssignment.isDefinitelyAssigned(), du); + } + for (PsiExpression dimension : newExpression.getArrayDimensions()) { + checkExpression(dimension, definiteAssignment, value); + } + final PsiArrayInitializerExpression arrayInitializer = newExpression.getArrayInitializer(); + if (arrayInitializer != null) { + checkExpression(arrayInitializer, definiteAssignment, value); + } + } + + private static void checkPolyadicExpression(PsiPolyadicExpression polyadicExpression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + final PsiExpression[] operands = polyadicExpression.getOperands(); + final IElementType tokenType = polyadicExpression.getOperationTokenType(); + if (JavaTokenType.OROR == tokenType || JavaTokenType.ANDAND == tokenType) { + for (int i = 1; i < operands.length; i++) { + checkBinaryExpression(operands[i - 1], operands[i], tokenType == JavaTokenType.ANDAND, definiteAssignment, value); + } + } + else { + for (PsiExpression operand : operands) { + checkExpression(operand, definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + } + } + + private static void checkBinaryExpression(PsiExpression lhs, + PsiExpression rhs, + boolean and, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + if (and ? value == BooleanExpressionValue.WHEN_FALSE : value == BooleanExpressionValue.WHEN_TRUE) { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + checkExpression(lhs, definiteAssignment, and ? BooleanExpressionValue.WHEN_FALSE : BooleanExpressionValue.WHEN_TRUE); + final boolean resultDa = definiteAssignment.isDefinitelyAssigned(); + final boolean resultDu = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(da, du); + checkExpression(lhs, definiteAssignment, and ? BooleanExpressionValue.WHEN_TRUE : BooleanExpressionValue.WHEN_FALSE); + checkExpression(rhs, definiteAssignment, and ? BooleanExpressionValue.WHEN_FALSE : BooleanExpressionValue.WHEN_TRUE); + definiteAssignment.and(resultDa, resultDu); + } + else if (and ? value == BooleanExpressionValue.WHEN_TRUE : value == BooleanExpressionValue.WHEN_FALSE) { + checkExpression(lhs, definiteAssignment, and ? BooleanExpressionValue.WHEN_TRUE : BooleanExpressionValue.WHEN_FALSE); + checkExpression(rhs, definiteAssignment, and ? BooleanExpressionValue.WHEN_TRUE : BooleanExpressionValue.WHEN_FALSE); + } + else { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + checkBinaryExpression(lhs, rhs, false, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + final boolean resultDa = definiteAssignment.isDefinitelyAssigned(); + final boolean resultDu = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(da, du); + checkBinaryExpression(lhs, rhs, false, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + definiteAssignment.and(resultDa, resultDu); + } + } + + private static void checkPostFixExpression(PsiPostfixExpression postfixExpression, DefiniteAssignment definiteAssignment) { + checkExpression(postfixExpression.getOperand(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + + private static void checkPrefixExpression(PsiPrefixExpression prefixExpression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + final IElementType tokenType = prefixExpression.getOperationTokenType(); + if (JavaTokenType.EXCL == tokenType) { + if (value == BooleanExpressionValue.WHEN_TRUE) { + checkExpression(prefixExpression.getOperand(), definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + } + else if (value == BooleanExpressionValue.WHEN_FALSE) { + checkExpression(prefixExpression.getOperand(), definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + } + else { + final boolean da = definiteAssignment.isDefinitelyAssigned(); + final boolean du = definiteAssignment.isDefinitelyUnassigned(); + checkPrefixExpression(prefixExpression, definiteAssignment, BooleanExpressionValue.WHEN_TRUE); + final boolean resultDa = definiteAssignment.isDefinitelyAssigned(); + final boolean resultDu = definiteAssignment.isDefinitelyUnassigned(); + definiteAssignment.set(da, du); + checkPrefixExpression(prefixExpression, definiteAssignment, BooleanExpressionValue.WHEN_FALSE); + definiteAssignment.and(resultDa, resultDu); + } + } + else { + checkExpression(prefixExpression.getOperand(), definiteAssignment, BooleanExpressionValue.UNDEFINED); + } + } + + private static void checkReferenceExpression(PsiReferenceExpression referenceExpression, + DefiniteAssignment definiteAssignment, + BooleanExpressionValue value) { + final PsiExpression qualifier = referenceExpression.getQualifierExpression(); + if (qualifier != null) { + checkExpression(qualifier, definiteAssignment, value); + } + if (!definiteAssignment.getVariable().equals(referenceExpression.resolve())) { + return; + } + if (PsiUtil.isAccessedForWriting(referenceExpression)) { + definiteAssignment.assign(referenceExpression, false); + } + else { + if (qualifier != null) { + if (!(qualifier instanceof PsiThisExpression)) { + return; + } + final PsiThisExpression thisExpression = (PsiThisExpression)qualifier; + if (thisExpression.getQualifier() != null) { + return; + } + } + definiteAssignment.valueAccess(referenceExpression); + } + } + + private enum BooleanExpressionValue { + WHEN_TRUE, WHEN_FALSE, UNDEFINED + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java index bf8c94d309df..1155a1083906 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FinalUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2011 Bas Leijdekkers + * Copyright 2009-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. @@ -16,650 +16,129 @@ package com.siyeh.ig.psiutils; 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.Nullable; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.NotNull; -import java.util.HashSet; -import java.util.Set; - -// todo handle variable initialization in loops public class FinalUtils { - private FinalUtils() { + private FinalUtils() {} + + public static boolean canBeFinal(@NotNull PsiVariable variable) { + final FinalDefiniteAssignment definiteAssignment = new FinalDefiniteAssignment(variable); + DefiniteAssignmentUtil.checkVariable(variable, definiteAssignment); + return definiteAssignment.isDefinitelyAssigned() && + !definiteAssignment.isDefinitelyUnassigned() && // spec? + definiteAssignment.canBeFinal() && + !isWrittenToOutsideOfConstruction(variable); } - public static boolean canBeFinal(PsiField field) { - PsiClass containingClass = field.getContainingClass(); - if (containingClass == null) { - return false; - } - final boolean fieldIsStatic = - field.hasModifierProperty(PsiModifier.STATIC); - final PsiField[] fields = containingClass.getFields(); - final DefiniteAssignmentVisitor visitor = - new DefiniteAssignmentVisitor(field); - if (field.hasInitializer()) { - visitor.setDefiniteAssignment(true, false); - } - for (PsiField aField : fields) { - final PsiExpression initializer = aField.getInitializer(); - if (initializer != null) { - initializer.accept(visitor); - if (!visitor.isFinalCandidate()) { - return false; - } - } - } - final PsiClassInitializer[] initializers = - containingClass.getInitializers(); - for (PsiClassInitializer initializer : initializers) { - initializer.accept(visitor); - if (!visitor.isFinalCandidate()) { - return false; - } - } - if (!fieldIsStatic) { - final boolean da = visitor.isDefinitelyAssigned(); - final boolean du = visitor.isDefinitelyUnassigned(); - final PsiMethod[] constructors = containingClass.getConstructors(); - for (PsiMethod constructor : constructors) { - final PsiCodeBlock body = constructor.getBody(); - if (body != null) { - visitor.setDefiniteAssignment(da, du); - body.accept(visitor); - } - if (!visitor.isDefinitelyAssigned()) { - return false; - } - if (!visitor.isFinalCandidate()) { - return false; - } - } - } - if (!visitor.isDefinitelyAssigned()) { + private static boolean isWrittenToOutsideOfConstruction(PsiVariable variable) { + if (!(variable instanceof PsiField)) { return false; } - if (!visitor.isFinalCandidate()) { - return false; - } - checkMembers(fieldIsStatic, containingClass, null, visitor); - if (!visitor.isFinalCandidate()) { + final PsiField field = (PsiField)variable; + final PsiClass containingClass = field.getContainingClass(); + if (containingClass == null) { return false; } - PsiClass aClass = containingClass.getContainingClass(); - while (aClass != null) { - visitor.setSkipClass(containingClass); - aClass.accept(visitor); - if (!visitor.isFinalCandidate()) { - return false; - } - containingClass = aClass; - aClass = containingClass.getContainingClass(); - } - return true; - } - - private static void checkMembers(boolean checkConstructors, - PsiClass containingClass, - @Nullable PsiClass skipClass, - DefiniteAssignmentVisitor visitor) { - final PsiMethod[] methods = containingClass.getMethods(); - for (PsiMethod method : methods) { - if (!checkConstructors && method.isConstructor()) { - continue; - } - method.accept(visitor); - if (!visitor.isFinalCandidate()) { - return; - } - } - final PsiClass[] innerClasses = containingClass.getInnerClasses(); - for (PsiClass innerClass : innerClasses) { - if (innerClass == skipClass) { - continue; - } - innerClass.accept(visitor); - if (!visitor.isFinalCandidate()) { - return; - } - } - } - - private static boolean isReadAccess(PsiReferenceExpression expression) { - final PsiElement parent = PsiTreeUtil.skipParentsOfType(expression, - PsiParenthesizedExpression.class); - if (!(parent instanceof PsiAssignmentExpression)) { - return true; - } - final PsiAssignmentExpression assignmentExpression = - (PsiAssignmentExpression)parent; - final PsiExpression lhs = assignmentExpression.getLExpression(); - if (!PsiTreeUtil.isAncestor(lhs, expression, false)) { - return true; - } - final IElementType tokenType = - assignmentExpression.getOperationTokenType(); - return tokenType != JavaTokenType.EQ; - } - - private static boolean isWriteAccess(PsiReferenceExpression expression) { - final PsiElement parent = - PsiTreeUtil.skipParentsOfType(expression, - PsiParenthesizedExpression.class); - if (parent instanceof PsiAssignmentExpression) { - final PsiAssignmentExpression assignmentExpression = - (PsiAssignmentExpression)parent; - final PsiExpression lhs = assignmentExpression.getLExpression(); - return PsiTreeUtil.isAncestor(lhs, expression, false); - } - else if (parent instanceof PsiPrefixExpression) { - final PsiPrefixExpression prefixExpression = - (PsiPrefixExpression)parent; - final IElementType tokenType = - prefixExpression.getOperationTokenType(); - return tokenType == JavaTokenType.PLUSPLUS || - tokenType == JavaTokenType.MINUSMINUS; - } - else if (parent instanceof PsiPostfixExpression) { - final PsiPostfixExpression postfixExpression = - (PsiPostfixExpression)parent; - final IElementType tokenType = - postfixExpression.getOperationTokenType(); - return tokenType == JavaTokenType.PLUSPLUS || - tokenType == JavaTokenType.MINUSMINUS; - } - return false; - } - - private static class DefiniteAssignmentVisitor - extends JavaRecursiveElementVisitor { - - private static final byte NOT_CONSTANT = 0; - private static final byte CONSTANT_TRUE = 1; - private static final byte CONSTANT_FALSE = 2; - - private final PsiField field; - private final Set<PsiStatement> exits = new HashSet(); - - private byte constant = NOT_CONSTANT; - private boolean definitelyAssigned = false; - private boolean definitelyUnassigned = true; - private boolean finalCandidate = true; - private PsiClass skipClass = null; - - private DefiniteAssignmentVisitor(PsiField field) { - this.field = field; - } - - public boolean isDefinitelyAssigned() { - return definitelyAssigned; - } - - public boolean isDefinitelyUnassigned() { - return definitelyUnassigned; - } - - public void setSkipClass(PsiClass skipClass) { - this.skipClass = skipClass; - } - - public void setDefiniteAssignment(boolean da, boolean du) { - definitelyAssigned = da; - definitelyUnassigned = du; - } - - public boolean isFinalCandidate() { - return finalCandidate; - } - - @Override - public void visitElement(PsiElement element) { - if (!isFinalCandidate()) { - return; - } - super.visitElement(element); - } - - @Override - public void visitMethod(PsiMethod method) { - definitelyAssigned = true; - definitelyUnassigned = false; - super.visitMethod(method); - } - - @Override - public void visitClass(PsiClass aClass) { - if (aClass == skipClass) { - return; - } - final boolean da = definitelyAssigned; - final boolean du = definitelyUnassigned; - definitelyAssigned = true; - definitelyUnassigned = false; - super.visitClass(aClass); - definitelyAssigned = da; - definitelyUnassigned = du; - } - - @Override - public void visitReferenceExpression(PsiReferenceExpression expression) { - super.visitReferenceExpression(expression); - if (PsiType.BOOLEAN.equals(expression.getType())) { - final Object constant = - ExpressionUtils.computeConstantExpression(expression); - if (Boolean.TRUE == constant) { - this.constant = CONSTANT_TRUE; - } - else if (Boolean.FALSE == constant) { - this.constant = CONSTANT_FALSE; - } - } - final PsiExpression qualifierExpression = - expression.getQualifierExpression(); - if (qualifierExpression != null && - !(qualifierExpression instanceof PsiThisExpression)) { - final PsiElement target = expression.resolve(); - if (!field.equals(target)) { - return; - } - if (isWriteAccess(expression)) { - finalCandidate = false; - } - return; - } - if (isPrePostFixExpression(expression)) { - final PsiElement target = expression.resolve(); - if (!field.equals(target)) { - return; - } - if (!definitelyAssigned || !definitelyUnassigned) { - finalCandidate = false; - } - else { - definitelyUnassigned = false; - } - } - else if (isReadAccess(expression)) { - final PsiElement target = expression.resolve(); - if (!field.equals(target)) { - return; - } - if (!definitelyAssigned) { - finalCandidate = false; + final PsiClass topLevelClass = PsiUtil.getTopLevelClass(variable); + final VariableAssignedVisitor visitor = new VariableAssignedVisitor(field); + if (topLevelClass != null && !containingClass.equals(topLevelClass)) { + visitor.setExcludedElement(containingClass); + topLevelClass.accept(visitor); + if (visitor.isAssigned()) { + return true; + } + } + if (field.hasModifierProperty(PsiModifier.STATIC)) { + for (PsiElement child : containingClass.getChildren()) { + if (child instanceof PsiClassInitializer) { + final PsiClassInitializer classInitializer = (PsiClassInitializer)child; + if (classInitializer.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + classInitializer.accept(visitor); } - } - } - - @Override - public void visitAssignmentExpression( - PsiAssignmentExpression expression) { - if (!finalCandidate) { - return; - } - final PsiExpression rhs = expression.getRExpression(); - if (rhs != null) { - rhs.accept(this); - } - final PsiExpression lhs = ParenthesesUtils.stripParentheses( - expression.getLExpression()); - if (!(lhs instanceof PsiReferenceExpression)) { - return; - } - final PsiReferenceExpression referenceExpression = - (PsiReferenceExpression)lhs; - final PsiExpression qualifierExpression = - referenceExpression.getQualifierExpression(); - if (qualifierExpression != null && - !(qualifierExpression instanceof PsiThisExpression)) { - visitReferenceExpression(referenceExpression); - return; - } - final PsiElement target = referenceExpression.resolve(); - if (!field.equals(target)) { - return; - } - final IElementType tokenType = expression.getOperationTokenType(); - if (!JavaTokenType.EQ.equals(tokenType)) { - finalCandidate = false; - } - if (definitelyUnassigned) { - definitelyAssigned = true; - definitelyUnassigned = false; - } - else { - finalCandidate = false; - } - } - - @Override - public void visitAssertStatement(PsiAssertStatement statement) { - final PsiExpression condition = statement.getAssertCondition(); - final boolean da = definitelyAssigned; - final boolean du = definitelyUnassigned; - if (condition != null) { - condition.accept(this); - } - final PsiExpression description = statement.getAssertDescription(); - if (description != null) { - description.accept(this); - } - definitelyAssigned &= da; - definitelyUnassigned &= du; - } - - @Override - public void visitLiteralExpression(PsiLiteralExpression expression) { - final Object value = expression.getValue(); - if (value instanceof Boolean) { - final Boolean aBoolean = (Boolean)value; - if (Boolean.TRUE == aBoolean) { - constant = CONSTANT_TRUE; + else if (child instanceof PsiField) { + final PsiField otherField = (PsiField)child; + if (otherField.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + otherField.accept(visitor); } - else if (Boolean.FALSE == aBoolean) { - constant = CONSTANT_FALSE; + else if (child instanceof PsiMethod || child instanceof PsiClass) { + child.accept(visitor); } - else { - throw new AssertionError(); + if (visitor.isAssigned()) { + return true; } } - else { - constant = NOT_CONSTANT; - } - } - - @Override - public void visitPrefixExpression(PsiPrefixExpression expression) { - final IElementType tokenType = expression.getOperationTokenType(); - if (JavaTokenType.EXCL != tokenType) { - - } - final PsiExpression operand = expression.getOperand(); - if (operand != null) { - operand.accept(this); - } - if (constant == CONSTANT_FALSE) { - constant = CONSTANT_TRUE; - } - else if (constant == CONSTANT_TRUE) { - constant = CONSTANT_FALSE; - } } - - @Override - public void visitWhileStatement(PsiWhileStatement statement) { - final PsiExpression condition = statement.getCondition(); - final PsiStatement body = statement.getBody(); - final boolean da = definitelyAssigned; - final boolean du = definitelyUnassigned; - for (int i = 0; i < 2; i++) { - if (condition != null) { - condition.accept(this); + else { + for (PsiElement child : containingClass.getChildren()) { + if (child instanceof PsiField) { + final PsiField otherField = (PsiField)child; + if (!otherField.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + otherField.accept(visitor); } - final byte constant = this.constant; - if (constant == CONSTANT_FALSE) { - satisfyVacuously(); + else if (child instanceof PsiClassInitializer) { + final PsiClassInitializer classInitializer = (PsiClassInitializer)child; + if (!classInitializer.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + classInitializer.accept(visitor); } - if (body != null) { - body.accept(this); + else if (child instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)child; + if (method.isConstructor()) { + continue; + } + method.accept(visitor); } - } - if (constant != CONSTANT_TRUE /*|| exits.remove(statement)*/) { - definitelyAssigned &= da; - definitelyUnassigned &= du; - } - } - - @Override - public void visitIfStatement(PsiIfStatement statement) { - final PsiExpression condition = statement.getCondition(); - constant = NOT_CONSTANT; - if (condition != null) { - condition.accept(this); - } - final byte constant = this.constant; - final PsiStatement thenBranch = statement.getThenBranch(); - final PsiStatement elseBranch = statement.getElseBranch(); - if (thenBranch == null) { - return; - } - final boolean da = definitelyAssigned; - final boolean du = definitelyUnassigned; - if (constant == CONSTANT_FALSE) { - satisfyVacuously(); - } - thenBranch.accept(this); - if (elseBranch == null) { - if (constant != CONSTANT_TRUE) { - definitelyAssigned &= da; - definitelyUnassigned &= du; + else if (child instanceof PsiClass) { + child.accept(visitor); } - return; - } - final boolean thenDa = definitelyAssigned; - final boolean thenDu = definitelyUnassigned; - definitelyAssigned = da; - definitelyUnassigned = du; - if (constant == CONSTANT_TRUE) { - satisfyVacuously(); - } - elseBranch.accept(this); - definitelyAssigned &= thenDa; - definitelyUnassigned &= thenDu; - } - - @Override - public void visitConditionalExpression( - PsiConditionalExpression expression) { - final PsiType type = expression.getType(); - final boolean booleanConditional = PsiType.BOOLEAN.equals(type); - final PsiExpression condition = expression.getCondition(); - constant = NOT_CONSTANT; - condition.accept(this); - final byte constant = this.constant; - final PsiExpression thenExpression = expression.getThenExpression(); - final PsiExpression elseExpression = expression.getElseExpression(); - final boolean da = definitelyAssigned; - final boolean du = definitelyUnassigned; - if (constant == CONSTANT_FALSE) { - satisfyVacuously(); - } - if (thenExpression != null) { - thenExpression.accept(this); - } - final boolean thenDa = definitelyAssigned; - final boolean thenDu = definitelyUnassigned; - definitelyAssigned = da; - definitelyUnassigned = du; - byte constantOut = NOT_CONSTANT; - if (constant == CONSTANT_TRUE) { - if (booleanConditional) { - constantOut = this.constant; + if (visitor.isAssigned()) { + return true; } - satisfyVacuously(); - } - if (elseExpression != null) { - elseExpression.accept(this); - } - if (constant == CONSTANT_TRUE) { - definitelyAssigned = thenDa; - definitelyUnassigned = thenDu; - this.constant = constantOut; - } - else if (constant != CONSTANT_FALSE) { - definitelyAssigned &= thenDa; - definitelyUnassigned &= thenDu; - this.constant = NOT_CONSTANT; } } + return false; + } - @Override - public void visitBinaryExpression(PsiBinaryExpression expression) { - final IElementType tokenType = expression.getOperationTokenType(); - if (JavaTokenType.ANDAND.equals(tokenType)) { - final PsiExpression lhs = expression.getLOperand(); - constant = NOT_CONSTANT; - lhs.accept(this); - final byte constant = this.constant; - final boolean da = definitelyAssigned; - final boolean du = definitelyUnassigned; - if (constant == CONSTANT_FALSE) { - satisfyVacuously(); - } - final PsiExpression rhs = expression.getROperand(); - if (rhs != null) { - rhs.accept(this); - } - if (this.constant == CONSTANT_TRUE) { - this.constant = constant; - } - else if (constant == CONSTANT_FALSE) { - this.constant = CONSTANT_FALSE; - definitelyAssigned = da; - definitelyUnassigned = du; - } - else if (constant == NOT_CONSTANT) { - this.constant = NOT_CONSTANT; - } - } - else if (JavaTokenType.OROR.equals(tokenType)) { - final PsiExpression lhs = expression.getLOperand(); - constant = NOT_CONSTANT; - lhs.accept(this); - final int constant = this.constant; - final boolean da = definitelyAssigned; - final boolean du = definitelyUnassigned; - if (constant == CONSTANT_TRUE) { - satisfyVacuously(); - } - final PsiExpression rhs = expression.getROperand(); - if (rhs != null) { - rhs.accept(this); - } - if (constant == CONSTANT_TRUE) { - this.constant = CONSTANT_TRUE; - } - else if (constant == NOT_CONSTANT) { - this.constant = NOT_CONSTANT; - } - if (constant == CONSTANT_TRUE) { - definitelyAssigned = da; - definitelyUnassigned = du; - } - } - else { - final PsiType type = expression.getType(); - if (PsiType.BOOLEAN.equals(type)) { - final Object constant = - ExpressionUtils.computeConstantExpression( - expression); - if (constant instanceof Boolean) { - if (Boolean.TRUE == constant) { - this.constant = CONSTANT_TRUE; - } - else if (Boolean.FALSE == constant) { - this.constant = CONSTANT_FALSE; - } - else { - this.constant = NOT_CONSTANT; - } - } - else { - this.constant = NOT_CONSTANT; - } - } - if (constant == NOT_CONSTANT) { - super.visitBinaryExpression(expression); - } - } - } + private static class FinalDefiniteAssignment extends DefiniteAssignment { - @Override - public void visitMethodCallExpression( - PsiMethodCallExpression expression) { - super.visitMethodCallExpression(expression); - final PsiReferenceExpression methodExpression = - expression.getMethodExpression(); - final PsiExpression qualifierExpression = - methodExpression.getQualifierExpression(); - if (qualifierExpression != null) { - return; - } - @NonNls final String referenceName = - methodExpression.getReferenceName(); - if (PsiKeyword.THIS.equals(referenceName)) { - definitelyUnassigned = false; - definitelyAssigned = true; - } - } + private boolean canBeFinal = true; - @Override - public void visitReturnStatement(PsiReturnStatement statement) { - final PsiExpression returnValue = statement.getReturnValue(); - if (returnValue != null) { - returnValue.accept(this); - } - if (!definitelyAssigned || definitelyUnassigned) { - finalCandidate = false; - } - satisfyVacuously(); + public FinalDefiniteAssignment(PsiVariable variable) { + super(variable); } @Override - public void visitThrowStatement(PsiThrowStatement statement) { - final PsiExpression exception = statement.getException(); - if (exception != null) { - exception.accept(this); + public void assign(@NotNull PsiReferenceExpression expression, boolean definiteAssignment) { + if (!isDefinitelyUnassigned()) { + canBeFinal = false; } - satisfyVacuously(); + super.assign(expression, definiteAssignment); } @Override - public void visitBreakStatement(PsiBreakStatement statement) { - final PsiStatement exit = statement.findExitedStatement(); - if (exit != null) { - exits.add(exit); + public void valueAccess(PsiReferenceExpression expression) { + if (!isDefinitelyAssigned()) { + canBeFinal = false; } - satisfyVacuously(); + super.valueAccess(expression); } @Override - public void visitContinueStatement(PsiContinueStatement statement) { - satisfyVacuously(); + public boolean stop() { + return !canBeFinal; } - private void satisfyVacuously() { - if (definitelyAssigned) { - definitelyUnassigned = true; - } - } - - private static boolean isPrePostFixExpression( - PsiReferenceExpression expression) { - final PsiElement parent = PsiTreeUtil.skipParentsOfType(expression, - PsiParenthesizedExpression.class); - if (parent instanceof PsiPrefixExpression) { - final PsiPrefixExpression prefixExpression = - (PsiPrefixExpression)parent; - final IElementType tokenType = - prefixExpression.getOperationTokenType(); - if (tokenType == JavaTokenType.PLUSPLUS || - tokenType == JavaTokenType.MINUSMINUS) { - return true; - } - } - else if (parent instanceof PsiPostfixExpression) { - final PsiPostfixExpression postfixExpression = - (PsiPostfixExpression)parent; - final IElementType tokenType = - postfixExpression.getOperationTokenType(); - if (tokenType == JavaTokenType.PLUSPLUS || - tokenType == JavaTokenType.MINUSMINUS) { - return true; - } - } - return false; + public boolean canBeFinal() { + return canBeFinal; } } }
\ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ImportUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ImportUtils.java index 93fb1d39159a..ddc5692cb1aa 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ImportUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ImportUtils.java @@ -19,17 +19,14 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleSettingsFacade; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.ClassUtil; import com.intellij.psi.util.InheritanceUtil; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.*; import com.siyeh.HardcodedMethodConstants; 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.*; public class ImportUtils { @@ -548,10 +545,26 @@ public class ImportUtils { /** * @return true, if the element contains a reference to a different class than fullyQualifiedName but which has the same class name */ - public static boolean containsConflictingReference(PsiElement element, String fullyQualifiedName) { + public static boolean containsConflictingReference(PsiFile element, String fullyQualifiedName) { + final Map<String, Boolean> cachedValue = + CachedValuesManager.getManager(element.getProject()).getCachedValue(element, new CachedValueProvider<Map<String, Boolean>>() { + @Nullable + @Override + public Result<Map<String, Boolean>> compute() { + return new Result<Map<String, Boolean>>(Collections.synchronizedMap(new HashMap<String, Boolean>()), PsiModificationTracker.MODIFICATION_COUNT); + } + }); + Boolean conflictingRef = cachedValue.get(fullyQualifiedName); + if (conflictingRef != null) { + return conflictingRef.booleanValue(); + } + final ConflictingClassReferenceVisitor visitor = new ConflictingClassReferenceVisitor(fullyQualifiedName); element.accept(visitor); - return visitor.isConflictingReferenceFound(); + conflictingRef = visitor.isConflictingReferenceFound(); + cachedValue.put(fullyQualifiedName, conflictingRef); + + return conflictingRef.booleanValue(); } private static class ConflictingClassReferenceVisitor extends JavaRecursiveElementVisitor { @@ -566,11 +579,17 @@ public class ImportUtils { } @Override + public void visitElement(PsiElement element) { + if (referenceFound) return; + super.visitElement(element); + } + + @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { - super.visitReferenceElement(reference); if (referenceFound) { return; } + super.visitReferenceElement(reference); if (reference.getQualifier() != null || reference.getParameterList() != null) return; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TypeUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TypeUtils.java index 30acc98df96a..5bb992dbbc90 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TypeUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TypeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 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. @@ -23,12 +23,9 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; - public class TypeUtils { - private TypeUtils() { - } + private TypeUtils() {} public static boolean typeEquals(@NonNls @NotNull String typeName, @Nullable PsiType targetType) { return targetType != null && targetType.equalsToText(typeName); @@ -57,6 +54,21 @@ public class TypeUtils { return typeEquals(CommonClassNames.JAVA_LANG_STRING, targetType); } + public static boolean isExpressionTypeAssignableWith(@NotNull PsiExpression expression, @NotNull Iterable<String> rhsTypeTexts) { + final PsiType type = expression.getType(); + if (type == null) { + return false; + } + final PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory(); + for (String rhsTypeText : rhsTypeTexts) { + final PsiClassType rhsType = factory.createTypeByFQClassName(rhsTypeText, expression.getResolveScope()); + if (type.isAssignableFrom(rhsType)) { + return true; + } + } + return false; + } + public static boolean expressionHasTypeOrSubtype(@Nullable PsiExpression expression, @NonNls @NotNull String typeName) { if (expression == null) { return false; @@ -98,7 +110,7 @@ public class TypeUtils { return null; } - public static boolean expressionHasTypeOrSubtype(@Nullable PsiExpression expression, @NonNls @NotNull Collection<String> typeNames) { + public static boolean expressionHasTypeOrSubtype(@Nullable PsiExpression expression, @NonNls @NotNull Iterable<String> typeNames) { if (expression == null) { return false; } @@ -148,9 +160,6 @@ public class TypeUtils { return false; } final PsiType type = expression.getType(); - if (type == null) { - return false; - } - return PsiType.FLOAT.equals(type) || PsiType.DOUBLE.equals(type); + return type != null && (PsiType.FLOAT.equals(type) || PsiType.DOUBLE.equals(type)); } }
\ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableAssignedVisitor.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableAssignedVisitor.java index 531b9a48f853..0a22ef3fde97 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableAssignedVisitor.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/VariableAssignedVisitor.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. @@ -20,55 +20,59 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.NotNull; -class VariableAssignedVisitor extends JavaRecursiveElementWalkingVisitor { +public class VariableAssignedVisitor extends JavaRecursiveElementWalkingVisitor { + @NotNull private final PsiVariable variable; private final boolean recurseIntoClasses; private final boolean checkUnaryExpressions; private boolean assigned = false; + private PsiElement excludedElement = null; - public VariableAssignedVisitor(@NotNull PsiVariable variable, - boolean recurseIntoClasses) { + public VariableAssignedVisitor(@NotNull PsiVariable variable, boolean recurseIntoClasses) { this.variable = variable; final PsiType type = variable.getType(); checkUnaryExpressions = TypeConversionUtil.isNumericType(type); this.recurseIntoClasses = recurseIntoClasses; } + public VariableAssignedVisitor(@NotNull PsiVariable variable) { + this(variable, true); + } + + public void setExcludedElement(PsiElement excludedElement) { + this.excludedElement = excludedElement; + } + @Override public void visitElement(@NotNull PsiElement element) { - if (assigned) { + if (assigned || element == excludedElement) { return; } super.visitElement(element); } @Override - public void visitAssignmentExpression( - @NotNull PsiAssignmentExpression assignment) { + public void visitAssignmentExpression(@NotNull PsiAssignmentExpression assignment) { if (assigned) { return; } super.visitAssignmentExpression(assignment); - final PsiExpression arg = assignment.getLExpression(); - if (VariableAccessUtils.mayEvaluateToVariable(arg, variable)) { + final PsiExpression lhs = assignment.getLExpression(); + if (VariableAccessUtils.evaluatesToVariable(lhs, variable)) { assigned = true; } } @Override public void visitClass(PsiClass aClass) { - if (!recurseIntoClasses) { - return; - } - if (assigned) { + if (!recurseIntoClasses || assigned) { return; } super.visitClass(aClass); } @Override - public void visitPrefixExpression( - @NotNull PsiPrefixExpression prefixExpression) { + public void visitPrefixExpression(@NotNull PsiPrefixExpression prefixExpression) { if (assigned) { return; } @@ -77,19 +81,17 @@ class VariableAssignedVisitor extends JavaRecursiveElementWalkingVisitor { return; } final IElementType tokenType = prefixExpression.getOperationTokenType(); - if (!tokenType.equals(JavaTokenType.PLUSPLUS) && - !tokenType.equals(JavaTokenType.MINUSMINUS)) { + if (!tokenType.equals(JavaTokenType.PLUSPLUS) && !tokenType.equals(JavaTokenType.MINUSMINUS)) { return; } final PsiExpression operand = prefixExpression.getOperand(); - if (VariableAccessUtils.mayEvaluateToVariable(operand, variable)) { + if (VariableAccessUtils.evaluatesToVariable(operand, variable)) { assigned = true; } } @Override - public void visitPostfixExpression( - @NotNull PsiPostfixExpression postfixExpression) { + public void visitPostfixExpression(@NotNull PsiPostfixExpression postfixExpression) { if (assigned) { return; } @@ -98,12 +100,11 @@ class VariableAssignedVisitor extends JavaRecursiveElementWalkingVisitor { return; } final IElementType tokenType = postfixExpression.getOperationTokenType(); - if (!tokenType.equals(JavaTokenType.PLUSPLUS) && - !tokenType.equals(JavaTokenType.MINUSMINUS)) { + if (!tokenType.equals(JavaTokenType.PLUSPLUS) && !tokenType.equals(JavaTokenType.MINUSMINUS)) { return; } final PsiExpression operand = postfixExpression.getOperand(); - if (VariableAccessUtils.mayEvaluateToVariable(operand, variable)) { + if (VariableAccessUtils.evaluatesToVariable(operand, variable)) { assigned = true; } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase.java index d9d8f483a7aa..b051e2e653a2 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase.java @@ -21,6 +21,7 @@ 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 com.siyeh.ig.ui.ExternalizableStringSet; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; @@ -29,11 +30,11 @@ import org.jetbrains.annotations.NotNull; import java.util.List; public class AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase extends BaseInspection { + public AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase() { - if (nonThreadSafeTypes.length() != 0) { + if (!nonThreadSafeTypes.isEmpty()) { nonThreadSafeClasses.clear(); - final List<String> strings = - StringUtil.split(nonThreadSafeTypes, ","); + final List<String> strings = StringUtil.split(nonThreadSafeTypes, ","); for (String string : strings) { nonThreadSafeClasses.add(string); } @@ -43,9 +44,8 @@ public class AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase extends @SuppressWarnings("PublicField") public final ExternalizableStringSet nonThreadSafeClasses = - new ExternalizableStringSet( - "java.text.SimpleDateFormat", - "java.util.Calendar"); + new ExternalizableStringSet("java.text.SimpleDateFormat", "java.util.Calendar"); + @NonNls @SuppressWarnings({"PublicField"}) public String nonThreadSafeTypes = ""; @@ -60,16 +60,13 @@ public class AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase extends @Nls @NotNull public String getDisplayName() { - return InspectionGadgetsBundle.message( - "access.to.non.thread.safe.static.field.from.instance.display.name"); + return InspectionGadgetsBundle.message("access.to.non.thread.safe.static.field.from.instance.display.name"); } @Override @NotNull protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "access.to.non.thread.safe.static.field.from.instance.field.problem.descriptor", - infos[0]); + return InspectionGadgetsBundle.message("access.to.non.thread.safe.static.field.from.instance.field.problem.descriptor", infos[0]); } @Override @@ -77,34 +74,31 @@ public class AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase extends return new AccessToNonThreadSafeStaticFieldFromInstanceVisitor(); } - class AccessToNonThreadSafeStaticFieldFromInstanceVisitor - extends BaseInspectionVisitor { + class AccessToNonThreadSafeStaticFieldFromInstanceVisitor extends BaseInspectionVisitor { @Override - public void visitReferenceExpression( - PsiReferenceExpression expression) { + public void visitReferenceExpression(PsiReferenceExpression expression) { super.visitReferenceExpression(expression); final PsiModifierListOwner parent = - PsiTreeUtil.getParentOfType(expression, - PsiField.class, PsiMethod.class, - PsiClassInitializer.class); + PsiTreeUtil.getParentOfType(expression, PsiField.class, PsiMethod.class, PsiClassInitializer.class); if (parent == null) { return; } - if (parent instanceof PsiMethod || - parent instanceof PsiClassInitializer) { + if (parent instanceof PsiMethod || parent instanceof PsiClassInitializer) { if (parent.hasModifierProperty(PsiModifier.SYNCHRONIZED)) { return; } - final PsiSynchronizedStatement synchronizedStatement = - PsiTreeUtil.getParentOfType(expression, - PsiSynchronizedStatement.class); + final PsiSynchronizedStatement synchronizedStatement = PsiTreeUtil.getParentOfType(expression, PsiSynchronizedStatement.class); if (synchronizedStatement != null) { return; } } - final PsiExpression qualifier = expression.getQualifierExpression(); - if (qualifier != null) { + if (parent instanceof PsiField || parent instanceof PsiClassInitializer) { + if (parent.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + } + if (expression.getQualifierExpression() != null) { return; } final PsiType type = expression.getType(); @@ -113,8 +107,12 @@ public class AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase extends } final PsiClassType classType = (PsiClassType)type; final String className = classType.rawType().getCanonicalText(); + boolean deepCheck = false; if (!nonThreadSafeClasses.contains(className)) { - return; + if (!TypeUtils.isExpressionTypeAssignableWith(expression, nonThreadSafeClasses)) { + return; + } + deepCheck = true; } final PsiElement target = expression.resolve(); if (!(target instanceof PsiField)) { @@ -124,7 +122,25 @@ public class AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase extends if (!field.hasModifierProperty(PsiModifier.STATIC)) { return; } - registerError(expression, className); + if (deepCheck) { + final PsiExpression initializer = field.getInitializer(); + if (initializer == null) { + return; + } + final PsiType initializerType = initializer.getType(); + if (!(initializerType instanceof PsiClassType)) { + return; + } + final PsiClassType classType2 = (PsiClassType)initializerType; + final String className2 = classType2.rawType().getCanonicalText(); + if (!nonThreadSafeClasses.contains(className2)) { + return; + } + registerError(expression, className2); + } + else { + registerError(expression, className); + } } } } |