summaryrefslogtreecommitdiff
path: root/java/java-analysis-impl/src/com/intellij/codeInspection
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2013-09-27 10:19:19 -0700
committerTor Norbye <tnorbye@google.com>2013-09-27 10:19:28 -0700
commitf7998d05c40c24ae66d1972abfcb070552b1d7b5 (patch)
treeb0b97491f6e6591e53ea6a8f6c3a79a7dc685771 /java/java-analysis-impl/src/com/intellij/codeInspection
parentbeca9839b2866f90da9dc517c29df2ec25a6f6a8 (diff)
downloadidea-f7998d05c40c24ae66d1972abfcb070552b1d7b5.tar.gz
Snapshot cd724ea5e27634f1c84f893f10b646937a677d56 from idea/132.425 of git://git.jetbrains.org/idea/community.git
cd724ea: Code cleanup - Idea's warnings fixed - message moved to .properties file c84855b: for performance use processNames api for java symbol contributor 334c509: IDEA-114064 Create "From Maven" library dialog doesn't handle full coords correctly (as advertised) 4e10a17: IDEA-109943 Download Library from Maven Repository: artifacts from repository with provider != maven2 are suggested, but repository is filtered off d4bc48d: IDEA-114037 Code completion should prefer variable names to unimported class names b7e5e6c: refix RUBY-11716: do not play with fire, always save and load in UTF-8 ! bb7ed8a: cleanup 15bd24e: cleanup e56b270: ProjectId, don't add project level provider if project is not mapped a0fb6a5: let IsNullCheck mean null->true, the former !null->false meaning was useless 26402542: test data fixed f800733: new inference: input/output inference variables 56d872d: NPE: find usage for db element 3ffd6a0: IDEA-114003 XDebuger breakpoint properties: strage selection jumps in breakpoint tree bbdef54: IDEA-114001 XDebuger Breakpoint Properties: enable in tree is not saved IDEA-114002 XDebuger breakpoint properties: don't close the dialog by double click IDEA-114004 XDebugger breakpoint properties: checkboxes synchronization is broken f6ff871: IDEA-114001 XDebuger Breakpoint Properties: enable in tree is not saved IDEA-114002 XDebuger breakpoint properties: don't close the dialog by double click IDEA-114004 XDebugger breakpoint properties: checkboxes synchronization is broken 8f3d962: IDEABKL-6897 Enter inserts closing brace in wrong place 6a78643: IDEABKL-6897 Enter inserts closing brace in wrong place 06b899c: IDEA-114001 XDebuger Breakpoint Properties: enable in tree is not saved IDEA-114002 XDebuger breakpoint properties: don't close the dialog by double click IDEA-114004 XDebugger breakpoint properties: checkboxes synchronization is broken aeda985: don't give focus to ant messages view f525708: new inference: overload resolution for SAM return type for implicit lambda should be ignored 6692602: cleanup current file from highlighting markup 27c6645: compilation fix 1936bb2: IDEA-107453 Compilation error if overriding methods have different access modifiers 0ddcc36: api a7f05e1: card layout fixed b007608: IDEA-110203 IDEA ignores space after comma setting for methods declaration/call [CR-IC-2022] ae02c17: hiding artifacts 90e6ed2: back to AddModuleWizard 4a029e9: Groovy: Pull-up members 6f9e810: NPE fc48bab: pull-up cbf7531: prepare pull-up refactoring for Groovy 6762d5f: cleanup b26ffb2: spaces around inner classes 41e8e70: IDEA-113333 Java formatter breaks source code: Merges statements into line comments [CR-IC-2245] ea91b55: IDEA-113815 keep "simple methods in one line" does not keep long methods [CR-IC-2486] ecd65ad: Import Eclipse code style settings from XML profile (a part of IDEA-104068) [CR-IC-2219] aacf1e4: IDEA-113844 (handling of core component initialization failures when spoiled by plugins) 8dc3ede: IDEA-112387 Reformat code with rearrange entires on = fail on enum [CR-IC-2205] 72ddb9e: Merge remote-tracking branch 'origin/master' 209340b: Add the description for MavenDuplicatePluginInspection. de367e1: Merge branch 'svn1_8_new' 1a70497: IdeaTestAssistant: fix test data d6a2510: svn: Fixed "Import" when path/url contains '@' symbol 94c4223: svn: Implemented "Export" action for command line 8913712: parameter popup: escape for annotation methods (IDEA-113971) fdf7312: svn: Made event handler for checkout/export utilize passed progress indicator (instead of just current thread progress indicator) f653713: IDEA-105758: Contradiction between error and fix actions (Java EE artifact) eac456f: Platform: ability to provide native icons for PsiElements (PsiFile/Directory) AppCode: blue icons for folders 4d5c47a: IDEA-77519: Project fails to open when workspace.xml is empty c40e732: two classes temporary restored to fix backward compatibility b6b3bc6: added optional dependencies for framework support providers a9dae78: IDEA-90661: recognize IBM JDK jars 6b44958: cleanup & javadoc 0c0a6a6: IDEA-108785 Allow applying the same context to many selected live templates 247661b: don't search for unknown path macros in the middle of xml unless asked so by PathMacroFilter (IDEA-102674) 90222d5: hopefully fix control flow building stack inconsistency assertions in case when a PCE is thrown 241e44b: EA-50288 - CCE: RefJavaUtilImpl.getTopLevelClass 22d5c2f: ensure to filter already inserted annotations (IDEA-113785) 27b76b4: inplace introducer: another case to restore expression (IDEA-113352) 603e138: add mnemonics (IDEA-113889) a8c374c: XSuspendPolicyPanel "Make default" (requires for new JavaBreakpointType) remove unused methods 4f19472: CR-IC-2485 (deprecation policy specified; deprecated API usage upgraded) 7231807: JavaBreakpointType isSuspendThreadSupported true 8637e89: register JavaBreakpointType, but hide under system property java.debugger.xBreakpoint 0b2d5f0: cleanup 5cc1f5e: EA-50289 (CCE: TooBroadScopeInspection$TooBroadScopeInspectionFix.getCommentText) 505b86e: new inference: pertinent to applicability 8ac21fd: cleanup 2c5f39e: cleanup f6efaa9: simplify DebuggerInvocationUtil bf8865f: overrides 0d49e5b: JavaBreakpointType canPutAt 33ebea3: extract XLineBreakpointTypeBase de7d963: extract XDebuggerEditorsProviderBase, init JavaBreakpointType (is not registered, so, not in action now) ed777e6: remove deprecated canPutAt 9163435: overrides 44af67f8: extract XDebuggerUtil.getInstance().getGroupingByFileRuleAsList() b93b27c: add missing Overrides 0857036: cleanup ae791a7: new inference: test preparations 48120ab: IdeaTestAssistant: add resolving and completion inside TestDataPath annotation 971544b: remove empty unused class c5642d9: hippie completion: split complex tokens by spaces e1383b1: refix and add test for IDEA-90294 Don't use substring match in word completion fbae94c: EA-49809 Made client factories final in SvnVcs 2b4b70f: Fix OC-8127: Appcode hangs on reformatting (endless right shift) +review CR-OC @Anton.Makeev, @Rustam.Vishnyakov b7314cf: fix pycharm detection 5f0bb3c: revert error checking 5f99590: svn: Implemented "Import" action for command line ad04905: svn: Changed import logic to use common commit event handler (instead of checkout event handler) 1a45be0: custom options 8d278a3: MavenArchetypesPanel extracted 32eeb4c: aggregation panel invisible 9388cc4: new project wizard: project type sorting 2c02a07: svn: Add "Skipped" event processing in commit/import output for command line d365580: svn: Refactored commit output parsing for command line b1cc312: immutable dfa offset stack 5c494fb: toolbarIcon is nullable 4304880: dfa: traverse only flushable variables, not all ed08eab: immutable EqClass 03de519: overrides f8661d9: return empty collection if list of storage files is empty 81a998c: IDEA-111030: Add Framework Support: Ok is disabled for the only selected Web Application 4ce1cc8: unused class removed 84f7401: svn: Added final status bar message for Import ("Committed revision xxx") aa1d479: library editor: 'attach javadoc' extracted to separate button so the main '+' button won't show popup 1612198: cleanup e180456: cleanup 546fffe: Merge remote-tracking branch 'origin/master' c3caf6c: Merge remote-tracking branch 'origin/master' 5cd2b37: assertion for EA-45385 - NPE: XmlTagImpl.getDescriptor 99563ea: EA-49418 - NPE: InjectLanguageAction.invokeImpl dc82859: cleanup f70ef96: EA-50139 - assert: TextRange.<init> 990e410: svn: Removed unused code (from "Ignore" functionality) 6604613: svn: Implemented "Ignore" functionality on subversion level d544d03: cleanup after notnullification 5330d9e: plugin suggester: suggest plugins from repository by unknown facet 1ccf92a: error which should never happen replaced by assertion 889d3ce: improved dialog for choosing root types of added roots 0eb34fa: hide "use out of process build" option from UI d79e13e: plugin suggester: suggest plugins from repository by unknown facet 00a127b: Use shell options only if applicable. b4f21b9: Don't fail to create SSH terminal session if we failed to create local terminal session. 8e5cb57: test for "Unnecessary unicode escape sequence" inspection dc8acce: temp revert c7c4431: fix SliceBackwardTest 3e16a04: svn: Implemented "Edit Revision Comment" action for command line c54948a: 'async' added e4f7950: svn: Correctly create externals that have '@' in url - add '@' at url end 9b66e1c: svn: Fix line separators duplication for "Create External" action for SVNKit a98dc14: svn: Implemented "Create External" action for command line 32e30d1: EA-50206 - assert: FileManagerImpl.findFile 4a4c0cc: svn: Unify line separator for multiline properties 4b9422b: Reverted: Semantic highlighting level to avoid conflicts with "unused symbol" annotations [CR-IC-2435] ad970ef: lambda: propagate wildcards elimination e78ab51: lambda: check formal params for equality, eliminate wildcards during inference according to 15.27.3 fc9a196: new inference: ignore proper types in mutual eq constraints generation 7bc0048: new inference: void compatible according to return values 939fc45: prepare for test new inference eef6eb2: new inference: exact method reference 172daec: new inference: eliminate delayed constrains according to 0.6.3 ec93384: postpone type evaluation e689d68: new inference: make use of ex constraint 7838f8e: Merge branch 'safe-sudo-escaping' 1c22df9: Add grails-app/resources as resources folder, not a source folder. a7f094d: fix NPE e1fe819: Added ExecUtil.sudoAndGetOutput() with safe escaping and quoting for Mac and Linux 6bf3a02: fixed EA-48905 - SIOOBE: ParameterInfoComponent$OneLineComponent.buildLabelText c853c69: system dependent paths in groovy shell db7d97f: Merge remote-tracking branch 'origin/master' 65426f9: mark as DumbAware 0d1a074: Support active links in GotIt panel c7297a0: debugging blinking test 755c6b2: IDEA-113938 "Submit feedback" should pre-fill project and affected version ff45141: dfa: don't go into the same instruction twice with the same state 3361944: a bit more parsimonous DfaMemoryStateImpl.createCopy 028d28b: dfa states should not change while in queue => no need to copy them 1709382: dfa: use UnorderedPair instead of two-element set e8dbc21: immutable DfaVariableState, for faster copying and less memory usage 666ed52: IDEA-70241 (Replace with '{@code}' inspection doesn't replace all occurrences in file.) 71bd9cf: Merge remote-tracking branch 'origin/master' c923098: EA-47881 - IOE: GroovyPsiElementFactoryImpl.createGroovyFileChecked 8b97940: cleanup logging 6e4b81e: EA-50137 - assert: TestObject.addClassesListToJavaParameters 5381d51: cleanup dc2d1b2: notnull f419fab: "todo" moved out of lang-impl 97cd633: attributes cleanup 75b3eaa: notnull c7fa9af: cleanup f5b5bf4: cleanup 776b16b: cleanup bf3cea4: made fields final 87913f7: cleanup 798e94e: cleanup, get rid of buggy duplicate node renderer 50f13e4: removed deprecated methods from ExternalAnnotator 6d9c887: avoid deprecated methods 4852116: minor 9cd549c: now Searchable. fixes test. 302302a: typo 26d8885: - handle strings with more than one quote used for start / end delimiters - proper retrieving syntax highlighter in case of non languge based syntaxhighlighter (quite often it is bound to file type) - Find: String literals only: Throwable at StringSearcher.scan() on XML with a string (IDEA-113885) - fix for backward search not ending when whole word option used 4dfd0c7: Encapsulate field dialog: explicit value for "Use accessors when field is accessible" when "as is" visibility is selected e1aff45: typo :((( 6237b9b: test fixed 6c0d31b: test fixed 493cfae: update test data 711bca1: update test data f1bc615: update test data 8dd18de: update test data e61a901: update test data 0be96e9: update test data aac178b: dfa: remove queued state duplication 6a25f5d: dfa: don't reschedule already processed states, cleanup 7559dfc: Semantic highlighting level to avoid conflicts with "unused symbol" annotations [CR-IC-2435], for WI-19396, WI-20126 (cherry picked from commit 56d66dc) 7c27aef: extract collectUsedJars() function 12f59ff: update test data 03df890: update test data 1efd604: update test data 2c1726e: update test data 24bf1d1: Merge remote-tracking branch 'origin/master' 8f26a8b: improved duplicates search in python extract method 9bfcbbe: setup resource roots when project is imported from Maven (IDEA-57398) 64594cd: diagnostics for EA-49831 0d76c32: add support for frameworks step: sorting restored b510334: IDEA-113294 indentation of brace in a lambda expression corrected [CR-IC-2426] 9651be4: IDEA-113910 Gradle: code insight; dependencies DSL resolving 25595c8: new inference: checked exceptions compatibility constraint 6b8f295: dump highlighting test data without markup 636719b: now that we have dfa state hashing, use it instead of linear lookup 3813120: dfa: don't merge states when there's only one 3fd9ba8: IDEA-113910 Gradle: code insight; dependencies DSL resolving 57a6aeb: IDEA-113910 Gradle: code insight; dependencies resolving da23b6c: Merge remote-tracking branch 'origin/master' 7ca6ea3: Merge remote branch 'origin/master' cdc6d6a: show deprecated make implementation warning once on first compilation after project opening 090c4e3: dfa state merging: cache copies e6ee024: Allows getting Gradle home without having a Project. 0391639: Gradle VM Options are now saved in between sessions. f5412f0: resource root: show 'New Directory' action instead of 'New Package' under resource roots 1637f6b: notnullification 87f394f: IDEA-113904 (Add New Module from Project Structure dialog wants to create new project) a7c75f6: IDEA-113865 ('Equals should check class of parameter' shouldn't warn on identity equals) 2486e9e: dfa: merge several states to account for variables with several possible values 4d4f4b0: dfa: some minor things and caching c14961f: UnorderedPair in platform 5ad442d: Merge remote-tracking branch 'origin/master' 946a281: Scroll to bottom on typing in terminal (PY-10344). e5a0548: Close all connections on dispose. f61f6a2: changes from tech-writers b82b0bb: dfa: state merging interruptibility 3893373: VcsDirtyScopeManagerImpl: log who marks everything dirty 41e5381: Merge remote-tracking branch 'origin/master' da21efb: JediTerm updated. beb1d1a: Merge remote-tracking branch 'origin/master' ed3a2b5: skip the whole document if some component has disabled roaming type (details CR-IU-308) d66309b: Fix antialiasing. e55fae1: dfa: abstract out eq class into EqClass class 370d44d: dfa: remove trivial state facts that constant != another constant 4ccfcf7: UsagesStatistic must specify roaming disabled 75894ae: cleanup 784b4e3: DimensionService: cleanup, order of stored data should be stable 36443b6: overrides b70b1dc: overrides dfe38fb: simplify some constant conditions and greenify 9a08478: IDEA-85961 (Pattern BACKSLASH_PATTERN = Pattern.compile("\\", Pattern.LITERAL) is always marked red.) d0bdc3b: simplify load from providers – we don't need to filter again (our save do it, in any case it is absurd to store component with global roaming in the project level file, — should be refine later) bf85e1b: cleanup 9c30823: remove unused methods f671c09: overrides 3bd62bc: ComponentRoamingManager should not keep defaults (we use RoamingType.PER_USER by default) a8f1063: CR-IU-300 remove outdated EP ComponentRoamingType b182751: CR-IU-300 remove outdated RoamingTypePerPlatform 994389c: another java.util.regex.Pattern.compile() parameter annotation 301710d: IDEA-113866 (this. not suggested for fields of anonymous inner classes) f5d03c4: dfa: don't consider final getters same as immutable fields 272a6d0: Corresponding parents for console colors. 71298ce: Bright console colors for Monokai. f39c34b: Console colors for WarmNeon scheme. 7548e47: Black is invisible on dark background (in RegExps for example). ca6b784: Change bloody red to light pink for numbers (pink is specific to Neon color schemes while red is not, also eyes say thanks). 7f627d9: Console colors for Twilight scheme fixed. a5eb2fc: Console colors for Monokai scheme fixed. f7da145: DfaPsiType: add @NotNull b216102: rebomb test b349547: diagnostics for inconsistencies during control flow building 6823425: dfa: only perform costly state merging when it has chances - after jumps fea8871: dfa: fighting too complex methods; join complementary memory states after fork to avoid having too many states 0ac2084: DfaMemoryStateImpl: introduce unwrap; compare variable values with their non-initialized counterparts e919b8b: dfa: only goto catch on non-trivial method calls and throws e2ea04f: new inference: initial method reference constraint a9dde36: new inference: check substituted descriptor return type 3e5b164: new inference: expression inside condition should be poly, target type for conditional expression ff9f2e9: new inference: emulate fresh variable - do not override vars with captured ones f415702: new inference: default constructor as poly expression argument ee56497: new inference: symmetric variable bounds 4a46b24: new inference: init inter call inference 06829c5: Merge remote-tracking branch 'origin/master' e3f213f: Console colors for Darcula. f748505: IDEA-57940 Cyclic expand word should take into account all open files 460ef47: StreamProvider.isVersioningRequired 8029f47: Bright yellow made more visible on white background. 96006be: Default console colors as in xterm palette, gray and dark gray from standard vga palette for better readability. a378414: Bight console colors added to settings. 54e6582: builder-based project types b0b2cf8: template-based project types? e741484: CPU hogging fix again eb22a99: fix todo duplication when several pattern match -> prefer finding match with last pattern (in settings list), thus default TODO pattern is matched last a4d4371: fix compilation 2f823ba: Difference Groovy Shell & Groovy Console actions 3e876f4: IDEA-113590 annotations as annotation values 732cafd: dead code a712f87: pull up 'isQualified()' method 4edd102: extract base class from PullUpDialog 10d89d6: remove obsolete test a0750e4: delete envFile manually 114cbb2: IDEA-113861 Gradle: it could be possible to hide 'Gradle: download' progress to the background ca416b8: 'More' element for classes, files, action, and settings. Better renderer. b2a550c: test framework: drop temp directory on light project close 8aaf53b: greenify ActionsTreeUtil a bit 922d41a: layout 2cd7326: adding frameworks support 2bd8fb0: cleanup 4ea6442: ProjectSummaryStep 9fdcdcd: commit project name bd39918: new project wizard: first test 630fc14: External system: use URLs in compile output paths 85dbe5f: IDEA-65114 "Add Maven projects" cannot be undone 5041ae5: test framework: returning of the data provider 16e2072: @Nullable XBreakpoint.getProperties reverted +review CR-IC-2418 df4da38: dump shell environment to a temporary file to reduce probability of malformed lines occurrence 280d52a: let event log warning color be orange (IDEA-113802) a30b2ae: IDEA-113836 Console folding: add TestNG related patterns cbeb0f1: don't change mouse cursor during goto name population (IDEA-113800) fb5ad5b: IDEA-113638 ChooseByName restart on write action spawns a new thread 89b3767: disable autopopup in groovy shell if selection by chars is enabled (IDEA-112820) 9fa36be: CompletionConfidence: don't force API users to implement unused method 34cf7eb: Console Folding: proper capitalization 5e8aa44: IDEA-113855 Search Everywhere looks scrambled at first start 71723cb: svn: Refactored executable validation - use separate version client 1114cfe: javadoc 0738800: jps model: simplification, source roots always have default properties b1d5062: jps model: JpsElementType converted to interface to allow reusing common base class 229deb0: test framework: ok, put light project file into ephemeral directory, but keep it for a project's life 6e8b950: ensure "thread" suspend policy for logging breakpoints d67c04e: EA-49809 Move client factories creation to SvnVcs constructor (instead of active() method) 935cdba: new inference: initial tests 970a180: encapsulate read access to USE_COMPILE_SERVER option b9b3fc8: remote agents - extract to remote servers cfe8e2b: WEB-9335 Bad insert pair brace in CSS 2815c22: WEB-9334 Incremental selection works bad with negative CSS values d023471: new "Unnecessary unicode escape sequence" inspection 945b069: unicode escape needs at least one 'u' 8e7797b: chrome still crashed, revert to old, not-recursive speed search 1917d86: Merge remote-tracking branch 'origin/master' c18e6bd: Lense mode "internal" preview db88427: Merge branch 'svn1_8_new' 7c64e8b: test framework: do not put light project file into ephemeral directory af09e11: remove @Nullable from key.get as it's too generic d49df32: svn: Refactored prompting for working copy format - make return not null format d6086ae: dfa: types with wildcard parameters are not equal 3d28aa5: Optimization: check exiting of griffon-app first b016c04: correct ephemeral state copy (IDEA-113143 Calling method with contract shouldn't result in nullability suspicion) 189573f: svn: Refactored upgrade working copy format dialog - use list of available formats (instead of separate fields for each format) 60c471b: Rename test 1a42f59: Remove using of unnecessary StringBuilder a56db90: Use MultiMap 340cf22: Calling method with contract shouldn't result in nullability suspicion (IDEA-113763, IDEA-113699, almost IDEA-113143) f2a563b: dfa: unify nullability violation processing 709af86: dfa: expand contract test 1cddfb4: dfa cleanup: skipping reports on method calls is now done via unknown variable mechanism 2765dab: dfa: spare some minor cpu cycles 8bc7465: svn: Added "1.8" option to upgrade/checkout dialogs c266bd2: IDEA-66603 Maven3: provide inspection that checks duplicate declarations of plugins af32923: SpellChecker: "cyclomatic" 5f4f98f: svn: Refactored working copy format selection dialog to use WorkingCopyFormat instances instead of just strings e33b58b: MismatchedCollectionQueryUpdateInspection -- added "compute" prefix (from j.u.Map in JDK8) 7d54b2e: new inference: accept nonProper eq bounds 457d95b: new inference: distinguish different captures 5b5f52e: new inference: assertions caused by raw types 3848de4: new inference: open top level captured wildcards 35d59c0: new inference: extend usage of already inferred variables 4f7b572: new inference: inference of calls in arguments during outer call inference bfa7879: new inference: captured variables from outer calls to be included ed95269: new inference: eq bound for S<=T constraint reduction 12a0faf: better positioning 13029b7: different position layout algorithms 39e6e21: EA-49923 - Fixed working copy format detection for default project d521e9d: JDK combobox should show JDK home 17fe05e: Fix typo 7ab769b: IDEA-16077 Maven embedder runs in the 'wrong' JDK add "embedder JDK" option 418ddc0: Inline string constant 6a8f1e2: IDEA-16077 Maven embedder runs in the 'wrong' JDK extract MavenJdkCombobox 46078c4: svn: Refactored detection if command line implementation should be used (use utility method) 24106fc: XDebugger: @Nullable XBreakpoint.getProperties() 3dc6f07: extract util method 476dac4: Reinit checkbox state on create 39cf1fd: Structure viewer for simple editors support fixed a38a251: Show filters only for table editor + a few model and UI fixes cbd5630: Initial dynamic filters model + columns header improved 281c008: svn: Support nullable SVNStatusClient in status implementation for SVNKit 1840d42: simplification fbcba0f: svn: Make command line clients (info, status) use vcs instead of project instances ae8b41b: Merge remote-tracking branch 'origin/master' a97397c: Extract method. 3b9eca4: new project wizard: preparing test infrastructure 327ddb0: Lense mode "internal" preview 0ede5e1: new project wizard: AbstractProjectWizard extracted a94b556: IDEA-111335 Gradle: task tree is incorrectly displayed if tasks are added to sub-projects via 'subprojects' method bfd366f: Merge remote-tracking branch 'origin/master' 0ef2a82: calls to obsolete method removed from build scripts 2df016f: AntLoggerFactory inner class made static to fix NoSuchMethodException in Logger.setFactory fe876e2: svn: Clients for update command renamed and moved to corresponding package a38d90e: Terminal color settings. bd285b8: svn: Refactored update logic to common ClientFactory model 9cc13da: platform: ignore hidden Windows files d87664c: test framework: relic property dropped 5f6ef06: svn: Removed unused "common ancestor" behavior from command line update client 3667fe1: Find in strings with reg exp with start / end match markers doesn't work without string delimiters (IDEA-113788) 94a079f: svn: Refactored "update" command - explicitly create new SVNKit update client for each update/switch operation 0344e91: new project wizard: added option to use framework libraries from an app server 2882ac0: svn: Removed unused methods from "update" client 17ea275: svn: Removed unused SvnProxies class c926181: WEB-9342 External Tool fails on OSX if an executable file basename specified a3433f9: svn: Removed unused methods from "info" client ec3112d: Optimize SassExtensionFunctionsIndex 9f1abf7: nosplash shouldn't prevent from plugin update 4c7154d: dfa: use cached nullability b81e9c3: dfa: less frequent "too long" check 0b0eba9: DfaValueFactory: use List instead of TIntObjectHashMap for sequential keys 95d5433: introduce DfaPsiType without nullability, to quickly check assignability/convertibility in DfaVariableState b901d90: IDEA-96713 Incorrect options shown for 'implements' e02e2c4: IDEA-113780 "Annotate" from history fixed for renamed/moved files fe00f73: test framework: stability improvements 76d664d: platform: suspicious event logging b204bdf: Cleanup (de-duplication) 7ba9ff0: spelling ece939c: make public for Upsource 496db4d: xdebugger: rebuild standalone variables view on EDT eae27cb: xdebugger: supported rebuilding of standalone variables view 8bf5204: xdebugger api: added convenient method 43dbb6f: Terminal options. 3db1f5c: Blink period and antialiasing settings. 88264bd: IDEA-108147 Use "merge sources" wrapper object instead of just string representation as data model for "Merge Sources" column (to correctly get file revision object and show details panel) 111e6ba: IDEA-108147 While building history for element make check "if element parent or child was changed in given revision" only be performed for non-"merge source" revisions e07828d: svn: Implemented support for "merged revisions" parsing in history logic for command line 1586c4a: svn: history logic refactored for command line - get and parse history data in xml format 0ac1dd2: IDEA-94942 Refactored "Annotate" implementation for command line - use utility method for parsing ba1e5e0: IDEA-94942 Implemented merge history support for "Annotate" action 4c3b0a8: IDEA-94942 Implemented "Switch" logic (during update) Change-Id: I7092ae66ff47d353a5b9770d1d91f77369bb7734
Diffstat (limited to 'java/java-analysis-impl/src/com/intellij/codeInspection')
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java268
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java176
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java92
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaInstructionState.java69
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java9
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java375
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java3
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java9
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java124
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/EqClass.java111
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java14
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java91
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java85
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java217
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java63
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java6
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BranchingInstruction.java14
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ConditionalGotoInstruction.java2
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GosubInstruction.java8
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GotoInstruction.java8
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/Instruction.java26
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaPsiType.java67
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaTypeValue.java65
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValue.java8
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java37
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java21
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java2
27 files changed, 1199 insertions, 771 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java
index 58bb1fc2cc1d..fed5b5b2e54a 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java
@@ -22,7 +22,6 @@ import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
@@ -100,24 +99,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
return JavaPsiFacade.getElementFactory(manager.getProject()).createTypeByFQClassName(fqn, scope);
}
- private boolean myRecursionStopper = false;
-
private <T extends Instruction> T addInstruction(T i) {
- ProgressManager.checkCanceled();
-
- if (!myRecursionStopper) {
- myRecursionStopper = true;
- try {
- // add extra conditional goto in order to handle possible runtime exceptions that could be caught by finally block
- if (i instanceof BranchingInstruction || i instanceof AssignInstruction || i instanceof MethodCallInstruction) {
- addConditionalRuntimeThrow();
- }
- }
- finally {
- myRecursionStopper = false;
- }
- }
-
myCurrentFlow.addInstruction(i);
return i;
}
@@ -137,7 +119,10 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
private void finishElement(PsiElement element) {
myCurrentFlow.finishElement(element);
- assert element == myElementStack.pop();
+ PsiElement popped = myElementStack.pop();
+ if (element != popped) {
+ throw new AssertionError("Expected " + element + ", popped " + popped);
+ }
}
@Override
@@ -146,64 +131,60 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
}
@Override public void visitAssignmentExpression(PsiAssignmentExpression expression) {
+ PsiExpression lExpr = expression.getLExpression();
+ PsiExpression rExpr = expression.getRExpression();
+
startElement(expression);
+ if (rExpr == null) {
+ pushUnknown();
+ finishElement(expression);
+ return;
+ }
- try {
- PsiExpression lExpr = expression.getLExpression();
- PsiExpression rExpr = expression.getRExpression();
+ lExpr.accept(this);
- if (rExpr == null) {
- pushUnknown();
- return;
+ IElementType op = expression.getOperationTokenType();
+ PsiType type = expression.getType();
+ boolean isBoolean = PsiType.BOOLEAN.equals(type);
+ if (op == JavaTokenType.EQ) {
+ rExpr.accept(this);
+ generateBoxingUnboxingInstructionFor(rExpr, type);
+ }
+ else if (op == JavaTokenType.ANDEQ) {
+ if (isBoolean) {
+ generateNonLazyExpression(true, lExpr, rExpr, type);
}
-
- lExpr.accept(this);
-
- IElementType op = expression.getOperationTokenType();
- PsiType type = expression.getType();
- boolean isBoolean = PsiType.BOOLEAN.equals(type);
- if (op == JavaTokenType.EQ) {
- rExpr.accept(this);
- generateBoxingUnboxingInstructionFor(rExpr, type);
- }
- else if (op == JavaTokenType.ANDEQ) {
- if (isBoolean) {
- generateNonLazyExpression(true, lExpr, rExpr, type);
- }
- else {
- generateDefaultBinOp(lExpr, rExpr, type);
- }
+ else {
+ generateDefaultBinOp(lExpr, rExpr, type);
}
- else if (op == JavaTokenType.OREQ) {
- if (isBoolean) {
- generateNonLazyExpression(false, lExpr, rExpr, type);
- }
- else {
- generateDefaultBinOp(lExpr, rExpr, type);
- }
+ }
+ else if (op == JavaTokenType.OREQ) {
+ if (isBoolean) {
+ generateNonLazyExpression(false, lExpr, rExpr, type);
}
- else if (op == JavaTokenType.XOREQ) {
- if (isBoolean) {
- generateXorExpression(expression, new PsiExpression[]{lExpr, rExpr}, type);
- }
- else {
- generateDefaultBinOp(lExpr, rExpr, type);
- }
+ else {
+ generateDefaultBinOp(lExpr, rExpr, type);
}
- else if (op == JavaTokenType.PLUSEQ && type != null && type.equalsToText(JAVA_LANG_STRING)) {
- lExpr.accept(this);
- rExpr.accept(this);
- addInstruction(new BinopInstruction(JavaTokenType.PLUS, null, lExpr.getProject()));
+ }
+ else if (op == JavaTokenType.XOREQ) {
+ if (isBoolean) {
+ generateXorExpression(expression, new PsiExpression[]{lExpr, rExpr}, type);
}
else {
generateDefaultBinOp(lExpr, rExpr, type);
}
-
- addInstruction(new AssignInstruction(rExpr));
}
- finally {
- finishElement(expression);
+ else if (op == JavaTokenType.PLUSEQ && type != null && type.equalsToText(JAVA_LANG_STRING)) {
+ lExpr.accept(this);
+ rExpr.accept(this);
+ addInstruction(new BinopInstruction(JavaTokenType.PLUS, null, lExpr.getProject()));
}
+ else {
+ generateDefaultBinOp(lExpr, rExpr, type);
+ }
+
+ addInstruction(new AssignInstruction(rExpr));
+ finishElement(expression);
}
private void generateDefaultBinOp(PsiExpression lExpr, PsiExpression rExpr, final PsiType exprType) {
@@ -658,11 +639,13 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
if (exception != null) {
exception.accept(this);
+ addConditionalRuntimeThrow();
addInstruction(new DupInstruction());
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, statement.getProject()));
ConditionalGotoInstruction gotoInstruction = new ConditionalGotoInstruction(null, true, null);
addInstruction(gotoInstruction);
+ addInstruction(new PushInstruction(myFactory.createTypeValue(myNpe, Nullness.NOT_NULL), null));
addThrowCode(myNpe);
gotoInstruction.setOffset(myCurrentFlow.getInstructionCount());
addThrowCode(exception.getType());
@@ -1008,42 +991,40 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
public void visitPolyadicExpression(PsiPolyadicExpression expression) {
startElement(expression);
- try {
- DfaValue dfaValue = myFactory.createValue(expression);
- if (dfaValue != null) {
- addInstruction(new PushInstruction(dfaValue, expression));
- return;
- }
- IElementType op = expression.getOperationTokenType();
-
- PsiExpression[] operands = expression.getOperands();
- if (operands.length <= 1) {
- pushUnknown();
- return;
- }
- PsiType type = expression.getType();
- if (op == JavaTokenType.ANDAND) {
- generateAndExpression(operands, type, true);
- }
- else if (op == JavaTokenType.OROR) {
- generateOrExpression(operands, type, true);
- }
- else if (op == JavaTokenType.XOR && PsiType.BOOLEAN.equals(type)) {
- generateXorExpression(expression, operands, type);
- }
- else if (op == JavaTokenType.AND && PsiType.BOOLEAN.equals(type)) {
- generateAndExpression(operands, type, false);
- }
- else if (op == JavaTokenType.OR && PsiType.BOOLEAN.equals(type)) {
- generateOrExpression(operands, type, false);
- }
- else {
- generateOther(expression, op, operands, type);
- }
+ DfaValue dfaValue = myFactory.createValue(expression);
+ if (dfaValue != null) {
+ addInstruction(new PushInstruction(dfaValue, expression));
+ finishElement(expression);
+ return;
}
- finally {
+ IElementType op = expression.getOperationTokenType();
+
+ PsiExpression[] operands = expression.getOperands();
+ if (operands.length <= 1) {
+ pushUnknown();
finishElement(expression);
+ return;
}
+ PsiType type = expression.getType();
+ if (op == JavaTokenType.ANDAND) {
+ generateAndExpression(operands, type, true);
+ }
+ else if (op == JavaTokenType.OROR) {
+ generateOrExpression(operands, type, true);
+ }
+ else if (op == JavaTokenType.XOR && PsiType.BOOLEAN.equals(type)) {
+ generateXorExpression(expression, operands, type);
+ }
+ else if (op == JavaTokenType.AND && PsiType.BOOLEAN.equals(type)) {
+ generateAndExpression(operands, type, false);
+ }
+ else if (op == JavaTokenType.OR && PsiType.BOOLEAN.equals(type)) {
+ generateOrExpression(operands, type, false);
+ }
+ else {
+ generateOther(expression, op, operands, type);
+ }
+ finishElement(expression);
}
private void generateOther(PsiPolyadicExpression expression, IElementType op, PsiExpression[] operands, PsiType type) {
@@ -1106,6 +1087,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
addInstruction(new MethodCallInstruction(expression, MethodCallInstruction.MethodType.UNBOXING, expectedType));
}
else if (TypeConversionUtil.isAssignableFromPrimitiveWrapper(expectedType) && TypeConversionUtil.isPrimitiveAndNotNull(exprType)) {
+ addConditionalRuntimeThrow();
addInstruction(new MethodCallInstruction(expression, MethodCallInstruction.MethodType.BOXING, expectedType));
}
else if (exprType != expectedType &&
@@ -1275,7 +1257,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
if (type instanceof PsiClassType) {
type = ((PsiClassType)type).rawType();
}
- addInstruction(new PushInstruction(myFactory.getTypeFactory().createTypeValue(type), null));
+ addInstruction(new PushInstruction(myFactory.createTypeValue(type, Nullness.UNKNOWN), null));
addInstruction(new InstanceofInstruction(expression, expression.getProject(), operand, type));
}
else {
@@ -1301,58 +1283,56 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
}
@Override public void visitMethodCallExpression(PsiMethodCallExpression expression) {
- try {
- startElement(expression);
+ startElement(expression);
- if (handleContracts(expression, getCallContracts(expression))) {
- return;
- }
+ if (handleContracts(expression, getCallContracts(expression))) {
+ finishElement(expression);
+ return;
+ }
- PsiReferenceExpression methodExpression = expression.getMethodExpression();
- PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
+ PsiReferenceExpression methodExpression = expression.getMethodExpression();
+ PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
- if (qualifierExpression != null) {
- qualifierExpression.accept(this);
- }
- else {
- pushUnknown();
- }
+ if (qualifierExpression != null) {
+ qualifierExpression.accept(this);
+ }
+ else {
+ pushUnknown();
+ }
- PsiExpression[] expressions = expression.getArgumentList().getExpressions();
- PsiElement method = methodExpression.resolve();
- PsiParameter[] parameters = method instanceof PsiMethod ? ((PsiMethod)method).getParameterList().getParameters() : null;
- for (int i = 0; i < expressions.length; i++) {
- PsiExpression paramExpr = expressions[i];
- paramExpr.accept(this);
- if (parameters != null && i < parameters.length) {
- generateBoxingUnboxingInstructionFor(paramExpr, parameters[i].getType());
- }
+ PsiExpression[] expressions = expression.getArgumentList().getExpressions();
+ PsiElement method = methodExpression.resolve();
+ PsiParameter[] parameters = method instanceof PsiMethod ? ((PsiMethod)method).getParameterList().getParameters() : null;
+ for (int i = 0; i < expressions.length; i++) {
+ PsiExpression paramExpr = expressions[i];
+ paramExpr.accept(this);
+ if (parameters != null && i < parameters.length) {
+ generateBoxingUnboxingInstructionFor(paramExpr, parameters[i].getType());
}
+ }
- addInstruction(new MethodCallInstruction(expression, createChainedVariableValue(expression)));
+ addConditionalRuntimeThrow();
+ addInstruction(new MethodCallInstruction(expression, createChainedVariableValue(expression)));
- if (!myCatchStack.isEmpty()) {
- addMethodThrows(expression.resolveMethod());
- }
+ if (!myCatchStack.isEmpty()) {
+ addMethodThrows(expression.resolveMethod());
+ }
- if (expressions.length == 1 && method instanceof PsiMethod &&
- "equals".equals(((PsiMethod)method).getName()) && parameters.length == 1 &&
- parameters[0].getType().equalsToText(JAVA_LANG_OBJECT) &&
- PsiType.BOOLEAN.equals(((PsiMethod)method).getReturnType())) {
- addInstruction(new PushInstruction(myFactory.getConstFactory().getFalse(), null));
- addInstruction(new SwapInstruction());
- addInstruction(new ConditionalGotoInstruction(getEndOffset(expression), true, null));
+ if (expressions.length == 1 && method instanceof PsiMethod &&
+ "equals".equals(((PsiMethod)method).getName()) && parameters.length == 1 &&
+ parameters[0].getType().equalsToText(JAVA_LANG_OBJECT) &&
+ PsiType.BOOLEAN.equals(((PsiMethod)method).getReturnType())) {
+ addInstruction(new PushInstruction(myFactory.getConstFactory().getFalse(), null));
+ addInstruction(new SwapInstruction());
+ addInstruction(new ConditionalGotoInstruction(getEndOffset(expression), true, null));
- addInstruction(new PopInstruction());
- addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null));
+ addInstruction(new PopInstruction());
+ addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null));
- expressions[0].accept(this);
- addInstruction(new ApplyNotNullInstruction(expression));
- }
- }
- finally {
- finishElement(expression);
+ expressions[0].accept(this);
+ addInstruction(new ApplyNotNullInstruction(expression));
}
+ finishElement(expression);
}
private boolean handleContracts(PsiMethodCallExpression expression, List<MethodContract> _contracts) {
@@ -1527,9 +1507,9 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
if (type == ASSERT_IS_NULL_METHOD || type == ASSERT_IS_NOT_NULL_METHOD) {
constraints[checkedParam] = type == ASSERT_IS_NOT_NULL_METHOD ? ValueConstraint.NULL_VALUE : ValueConstraint.NOT_NULL_VALUE;
return Collections.singletonList(new MethodContract(constraints, ValueConstraint.THROW_EXCEPTION));
- } else if (type == IS_NULL_METHOD || type == IS_NOT_NULL_METHOD) {
- constraints[checkedParam] = type == IS_NULL_METHOD ? ValueConstraint.NOT_NULL_VALUE : ValueConstraint.NULL_VALUE;
- return Collections.singletonList(new MethodContract(constraints, ValueConstraint.FALSE_VALUE));
+ } else if (type == IS_NOT_NULL_METHOD || type == IS_NULL_METHOD) {
+ constraints[checkedParam] = ValueConstraint.NULL_VALUE;
+ return Collections.singletonList(new MethodContract(constraints, type == IS_NULL_METHOD ? ValueConstraint.TRUE_VALUE : ValueConstraint.FALSE_VALUE));
} else { //assertTrue or assertFalse
constraints[checkedParam] = type == ASSERT_FALSE_METHOD ? ValueConstraint.TRUE_VALUE : ValueConstraint.FALSE_VALUE;
return Collections.singletonList(new MethodContract(constraints, ValueConstraint.THROW_EXCEPTION));
@@ -1594,7 +1574,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
final DfaValue dfaValue;
if (type instanceof PsiClassType) {
- dfaValue = myFactory.getTypeFactory().createTypeValue(type);
+ dfaValue = myFactory.createTypeValue(type, Nullness.UNKNOWN);
}
else {
dfaValue = null;
@@ -1623,6 +1603,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
addInstruction(new PopInstruction());
}
}
+ addConditionalRuntimeThrow();
addInstruction(new MethodCallInstruction(expression, null));
}
else {
@@ -1640,6 +1621,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
}
}
+ addConditionalRuntimeThrow();
addInstruction(new MethodCallInstruction(expression, null));
if (!myCatchStack.isEmpty()) {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java
index bbac522a4c5a..b462090126a2 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java
@@ -33,8 +33,10 @@ import com.intellij.codeInsight.intention.impl.AddNullableAnnotationFix;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
+import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
@@ -47,6 +49,7 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
@@ -145,7 +148,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
PsiClass containingClass = PsiTreeUtil.getParentOfType(scope, PsiClass.class);
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass)) return;
- final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(SUGGEST_NULLABLE_ANNOTATIONS) {
+ final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner() {
@Override
protected boolean shouldCheckTimeLimit() {
if (!onTheFly) return false;
@@ -159,7 +162,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
ProblemsHolder holder,
StandardDataFlowRunner dfaRunner,
Collection<DfaMemoryState> initialStates) {
- final StandardInstructionVisitor visitor = new DataFlowInstructionVisitor(dfaRunner);
+ final DataFlowInstructionVisitor visitor = new DataFlowInstructionVisitor(dfaRunner);
final RunnerResult rc = dfaRunner.analyzeMethod(scope, visitor, IGNORE_ASSERT_STATEMENTS, initialStates);
if (rc == RunnerResult.OK) {
createDescription(dfaRunner, holder, visitor);
@@ -213,7 +216,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
protected void addSurroundWithIfFix(PsiExpression qualifier, List<LocalQuickFix> fixes) {
}
- private void createDescription(StandardDataFlowRunner runner, ProblemsHolder holder, StandardInstructionVisitor visitor) {
+ private void createDescription(StandardDataFlowRunner runner, ProblemsHolder holder, DataFlowInstructionVisitor visitor) {
Pair<Set<Instruction>, Set<Instruction>> constConditions = runner.getConstConditionalExpressions();
Set<Instruction> trueSet = constConditions.getFirst();
Set<Instruction> falseSet = constConditions.getSecond();
@@ -221,28 +224,25 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
ArrayList<Instruction> allProblems = new ArrayList<Instruction>();
allProblems.addAll(trueSet);
allProblems.addAll(falseSet);
- allProblems.addAll(runner.getNPEInstructions());
allProblems.addAll(runner.getCCEInstructions());
allProblems.addAll(StandardDataFlowRunner.getRedundantInstanceofs(runner, visitor));
- Collections.sort(allProblems, new Comparator<Instruction>() {
- @Override
- public int compare(Instruction i1, Instruction i2) {
- return i1.getIndex() - i2.getIndex();
- }
- });
-
HashSet<PsiElement> reportedAnchors = new HashSet<PsiElement>();
-
- for (Instruction instruction : allProblems) {
- if (instruction instanceof MethodCallInstruction) {
- reportCallMayProduceNpe(holder, (MethodCallInstruction)instruction, reportedAnchors);
+ for (PsiElement element : visitor.getProblems(NullabilityProblem.callNPE)) {
+ if (reportedAnchors.add(element)) {
+ reportCallMayProduceNpe(holder, (PsiMethodCallExpression)element);
}
- else if (instruction instanceof FieldReferenceInstruction &&
- reportedAnchors.add(((FieldReferenceInstruction)instruction).getElementToAssert())) {
- reportFieldAccessMayProduceNpe(holder, (FieldReferenceInstruction)instruction);
+ }
+ for (PsiElement element : visitor.getProblems(NullabilityProblem.fieldAccessNPE)) {
+ if (reportedAnchors.add(element)) {
+ PsiElement parent = element.getParent();
+ PsiElement fieldAccess = parent instanceof PsiArrayAccessExpression || parent instanceof PsiReferenceExpression ? parent : element;
+ reportFieldAccessMayProduceNpe(holder, element, (PsiExpression)fieldAccess);
}
- else if (instruction instanceof TypeCastInstruction &&
+ }
+
+ for (Instruction instruction : allProblems) {
+ if (instruction instanceof TypeCastInstruction &&
reportedAnchors.add(((TypeCastInstruction)instruction).getCastExpression().getCastType())) {
reportCastMayFail(holder, (TypeCastInstruction)instruction);
}
@@ -251,11 +251,15 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
}
- reportNullableArguments(runner, holder, reportedAnchors);
- reportNullableAssignments(runner, holder, reportedAnchors);
- reportUnboxedNullables(runner, holder, reportedAnchors);
- reportNullableReturns(runner, holder, reportedAnchors);
- reportNullableArgumentsPassedToNonAnnotated(runner, holder, reportedAnchors);
+ reportNullableArguments(visitor, holder, reportedAnchors);
+ reportNullableAssignments(visitor, holder, reportedAnchors);
+ reportUnboxedNullables(visitor, holder, reportedAnchors);
+ if (!runner.isInNullableMethod() && runner.isInMethod() && (runner.isInNotNullMethod() || SUGGEST_NULLABLE_ANNOTATIONS)) {
+ reportNullableReturns(runner, visitor, holder, reportedAnchors);
+ }
+ if (SUGGEST_NULLABLE_ANNOTATIONS) {
+ reportNullableArgumentsPassedToNonAnnotated(visitor, holder, reportedAnchors);
+ }
if (REPORT_CONSTANT_REFERENCE_VALUES) {
reportConstantReferenceValues(holder, visitor, reportedAnchors);
@@ -317,15 +321,14 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
return value instanceof String ? "\"" + StringUtil.escapeStringCharacters((String)value) + "\"" : String.valueOf(value);
}
- private void reportNullableArgumentsPassedToNonAnnotated(StandardDataFlowRunner runner, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
- Set<PsiExpression> exprs = runner.getNullableArgumentsPassedToNonAnnotatedParam();
- for (PsiExpression expr : exprs) {
+ private void reportNullableArgumentsPassedToNonAnnotated(DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
+ for (PsiElement expr : visitor.getProblems(NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter)) {
if (reportedAnchors.contains(expr)) continue;
final String text = isNullLiteralExpression(expr)
? "Passing <code>null</code> argument to non annotated parameter"
: "Argument <code>#ref</code> #loc might be null but passed to non annotated parameter";
- LocalQuickFix[] fixes = createNPEFixes(expr, expr);
+ LocalQuickFix[] fixes = createNPEFixes((PsiExpression)expr, (PsiExpression)expr);
final PsiElement parent = expr.getParent();
if (parent instanceof PsiExpressionList) {
final int idx = ArrayUtilRt.find(((PsiExpressionList)parent).getExpressions(), expr);
@@ -349,22 +352,15 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
}
- private void reportCallMayProduceNpe(ProblemsHolder holder, MethodCallInstruction mcInstruction, Set<PsiElement> reportedAnchors) {
- if (mcInstruction.getCallExpression() instanceof PsiMethodCallExpression) {
- PsiMethodCallExpression callExpression = (PsiMethodCallExpression)mcInstruction.getCallExpression();
- if (!reportedAnchors.add(callExpression)) return;
-
- LocalQuickFix[] fix = createNPEFixes(callExpression.getMethodExpression().getQualifierExpression(), callExpression);
+ private void reportCallMayProduceNpe(ProblemsHolder holder, PsiMethodCallExpression callExpression) {
+ LocalQuickFix[] fix = createNPEFixes(callExpression.getMethodExpression().getQualifierExpression(), callExpression);
- holder.registerProblem(callExpression,
- InspectionsBundle.message("dataflow.message.npe.method.invocation"),
- fix);
- }
+ holder.registerProblem(callExpression,
+ InspectionsBundle.message("dataflow.message.npe.method.invocation"),
+ fix);
}
- private void reportFieldAccessMayProduceNpe(ProblemsHolder holder, FieldReferenceInstruction frInstruction) {
- PsiElement elementToAssert = frInstruction.getElementToAssert();
- PsiExpression expression = frInstruction.getExpression();
+ private void reportFieldAccessMayProduceNpe(ProblemsHolder holder, PsiElement elementToAssert, PsiExpression expression) {
if (expression instanceof PsiArrayAccessExpression) {
LocalQuickFix[] fix = createNPEFixes((PsiExpression)elementToAssert, expression);
holder.registerProblem(expression,
@@ -382,8 +378,11 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
private static void reportCastMayFail(ProblemsHolder holder, TypeCastInstruction instruction) {
PsiTypeCastExpression typeCast = instruction.getCastExpression();
- holder.registerProblem(typeCast.getCastType(),
- InspectionsBundle.message("dataflow.message.cce", typeCast.getOperand().getText()));
+ PsiExpression operand = typeCast.getOperand();
+ PsiTypeElement castType = typeCast.getCastType();
+ assert castType != null;
+ assert operand != null;
+ holder.registerProblem(castType, InspectionsBundle.message("dataflow.message.cce", operand.getText()));
}
private void handleBranchingInstruction(ProblemsHolder holder,
@@ -413,7 +412,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
else if (psiAnchor != null && !reportedAnchors.contains(psiAnchor) && !isCompileConstantInIfCondition(psiAnchor)) {
boolean evaluatesToTrue = trueSet.contains(instruction);
- if (onTheLeftSideOfConditionalAssignemnt(psiAnchor)) {
+ if (onTheLeftSideOfConditionalAssignment(psiAnchor)) {
holder.registerProblem(
psiAnchor,
InspectionsBundle.message("dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)),
@@ -436,21 +435,20 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
visitor.silenceConstantCondition(psiAnchor);
}
- private void reportNullableArguments(StandardDataFlowRunner runner, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
- Set<PsiExpression> exprs = runner.getNullableArguments();
- for (PsiExpression expr : exprs) {
+ private void reportNullableArguments(DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
+ for (PsiElement expr : visitor.getProblems(NullabilityProblem.passingNullableToNotNullParameter)) {
if (!reportedAnchors.add(expr)) continue;
final String text = isNullLiteralExpression(expr)
? InspectionsBundle.message("dataflow.message.passing.null.argument")
: InspectionsBundle.message("dataflow.message.passing.nullable.argument");
- LocalQuickFix[] fixes = createNPEFixes(expr, expr);
+ LocalQuickFix[] fixes = createNPEFixes((PsiExpression)expr, (PsiExpression)expr);
holder.registerProblem(expr, text, fixes);
}
}
- private static void reportNullableAssignments(StandardDataFlowRunner runner, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
- for (PsiExpression expr : runner.getNullableAssignments()) {
+ private static void reportNullableAssignments(DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
+ for (PsiElement expr : visitor.getProblems(NullabilityProblem.assigningToNotNull)) {
if (!reportedAnchors.add(expr)) continue;
final String text = isNullLiteralExpression(expr)
@@ -460,16 +458,16 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
}
- private static void reportUnboxedNullables(StandardDataFlowRunner runner, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
- for (PsiExpression expr : runner.getUnboxedNullables()) {
+ private static void reportUnboxedNullables(DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
+ for (PsiElement expr : visitor.getProblems(NullabilityProblem.unboxingNullable)) {
if (!reportedAnchors.add(expr)) continue;
holder.registerProblem(expr, InspectionsBundle.message("dataflow.message.unboxing"));
}
}
- private static void reportNullableReturns(StandardDataFlowRunner runner, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
- for (PsiReturnStatement statement : runner.getNullableReturns()) {
- final PsiExpression expr = statement.getReturnValue();
+ private static void reportNullableReturns(StandardDataFlowRunner runner, DataFlowInstructionVisitor visitor, ProblemsHolder holder, Set<PsiElement> reportedAnchors) {
+ for (PsiElement statement : visitor.getProblems(NullabilityProblem.nullableReturn)) {
+ final PsiExpression expr = ((PsiReturnStatement)statement).getReturnValue();
assert expr != null;
if (!reportedAnchors.add(expr)) continue;
@@ -547,7 +545,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
return parent instanceof PsiIfStatement && ((PsiIfStatement)parent).getCondition() == element;
}
- private static boolean isNullLiteralExpression(PsiExpression expr) {
+ private static boolean isNullLiteralExpression(PsiElement expr) {
if (expr instanceof PsiLiteralExpression) {
final PsiLiteralExpression literalExpression = (PsiLiteralExpression)expr;
return PsiType.NULL.equals(literalExpression.getType());
@@ -555,7 +553,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
return false;
}
- private static boolean onTheLeftSideOfConditionalAssignemnt(final PsiElement psiAnchor) {
+ private static boolean onTheLeftSideOfConditionalAssignment(final PsiElement psiAnchor) {
final PsiElement parent = psiAnchor.getParent();
if (parent instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression expression = (PsiAssignmentExpression)parent;
@@ -699,45 +697,55 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
private static class DataFlowInstructionVisitor extends StandardInstructionVisitor {
private final StandardDataFlowRunner myRunner;
+ private final MultiMap<NullabilityProblem, PsiElement> myProblems = new MultiMap<NullabilityProblem, PsiElement>();
+ private final Map<Pair<NullabilityProblem, PsiElement>, StateInfo> myStateInfos = ContainerUtil.newHashMap();
private DataFlowInstructionVisitor(StandardDataFlowRunner runner) {
myRunner = runner;
}
@Override
- protected void onAssigningToNotNullableVariable(AssignInstruction instruction) {
- myRunner.onAssigningToNotNullableVariable(instruction.getRExpression());
- }
-
- @Override
- protected void onNullableReturn(CheckReturnValueInstruction instruction) {
- myRunner.onNullableReturn(instruction.getReturn());
- }
-
- @Override
protected void onInstructionProducesCCE(TypeCastInstruction instruction) {
myRunner.onInstructionProducesCCE(instruction);
}
-
- @Override
- protected void onInstructionProducesNPE(Instruction instruction) {
- if (instruction instanceof MethodCallInstruction &&
- ((MethodCallInstruction)instruction).getMethodType() == MethodCallInstruction.MethodType.UNBOXING) {
- myRunner.onUnboxingNullable(((MethodCallInstruction)instruction).getContext());
- }
- else {
- myRunner.onInstructionProducesNPE(instruction);
- }
- }
-
- @Override
- protected void onPassingNullParameter(PsiExpression arg) {
- myRunner.onPassingNullParameter(arg);
+
+ Collection<PsiElement> getProblems(final NullabilityProblem kind) {
+ return ContainerUtil.filter(myProblems.get(kind), new Condition<PsiElement>() {
+ @Override
+ public boolean value(PsiElement psiElement) {
+ StateInfo info = myStateInfos.get(Pair.create(kind, psiElement));
+ // non-ephemeral NPE should be reported
+ // ephemeral NPE should also be reported if only ephemeral states have reached a particular problematic instruction
+ // (e.g. if it's inside "if (var == null)" check after contract method invocation
+ return info.normalNpe || info.ephemeralNpe && !info.normalOk;
+ }
+ });
}
@Override
- protected void onPassingNullParameterToNonAnnotated(DataFlowRunner runner, PsiExpression arg) {
- myRunner.onPassingNullParameterToNonAnnotated(arg);
+ protected boolean checkNotNullable(DfaMemoryState state, DfaValue value, NullabilityProblem problem, PsiElement anchor) {
+ boolean ok = super.checkNotNullable(state, value, problem, anchor);
+ if (!ok && anchor != null) {
+ myProblems.putValue(problem, anchor);
+ }
+ Pair<NullabilityProblem, PsiElement> key = Pair.create(problem, anchor);
+ StateInfo info = myStateInfos.get(key);
+ if (info == null) {
+ myStateInfos.put(key, info = new StateInfo());
+ }
+ if (state.isEphemeral() && !ok) {
+ info.ephemeralNpe = true;
+ } else if (!state.isEphemeral()) {
+ if (ok) info.normalOk = true;
+ else info.normalNpe = true;
+ }
+ return ok;
+ }
+
+ private static class StateInfo {
+ boolean ephemeralNpe;
+ boolean normalNpe;
+ boolean normalOk;
}
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java
index 298b51bc4a24..38c242bc8e2a 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java
@@ -24,10 +24,7 @@
*/
package com.intellij.codeInspection.dataFlow;
-import com.intellij.codeInspection.dataFlow.instructions.BranchingInstruction;
-import com.intellij.codeInspection.dataFlow.instructions.EmptyInstruction;
-import com.intellij.codeInspection.dataFlow.instructions.Instruction;
-import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction;
+import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.openapi.application.ApplicationManager;
@@ -38,7 +35,9 @@ import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
+import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
+import com.intellij.util.containers.MultiMapBasedOnSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -108,6 +107,17 @@ public class DataFlowRunner {
myInstructions = flow.getInstructions();
myFields = flow.getFields();
myNestedClosures.clear();
+
+ Set<Instruction> joinInstructions = ContainerUtil.newHashSet();
+ for (Instruction instruction : myInstructions) {
+ if (instruction instanceof GotoInstruction) {
+ joinInstructions.add(myInstructions[((GotoInstruction)instruction).getOffset()]);
+ } else if (instruction instanceof ConditionalGotoInstruction) {
+ joinInstructions.add(myInstructions[((ConditionalGotoInstruction)instruction).getOffset()]);
+ } else if (instruction instanceof GosubInstruction) {
+ joinInstructions.add(myInstructions[((GosubInstruction)instruction).getSubprogramOffset()]);
+ }
+ }
if (LOG.isDebugEnabled()) {
LOG.debug("Analyzing code block: " + psiBlock.getText());
@@ -123,47 +133,61 @@ public class DataFlowRunner {
return RunnerResult.TOO_COMPLEX;
}
- final ArrayList<DfaInstructionState> queue = new ArrayList<DfaInstructionState>();
+ final StateQueue queue = new StateQueue();
for (final DfaMemoryState initialState : initialStates) {
- queue.add(new DfaInstructionState(myInstructions[0], initialState));
+ queue.offer(new DfaInstructionState(myInstructions[0], initialState));
}
+ MultiMapBasedOnSet<BranchingInstruction, DfaMemoryState> processedStates = new MultiMapBasedOnSet<BranchingInstruction, DfaMemoryState>();
+ MultiMapBasedOnSet<BranchingInstruction, DfaMemoryState> incomingStates = new MultiMapBasedOnSet<BranchingInstruction, DfaMemoryState>();
+
WorkingTimeMeasurer measurer = new WorkingTimeMeasurer(shouldCheckTimeLimit() ? ourTimeLimit : ourTimeLimit * 42);
int count = 0;
while (!queue.isEmpty()) {
- if (count % 64 == 0 && measurer.isTimeOver()) {
- LOG.debug("Too complex because the analysis took too long");
- psiBlock.putUserData(TOO_EXPENSIVE_HASH, psiBlock.getText().hashCode());
- return RunnerResult.TOO_COMPLEX;
- }
- ProgressManager.checkCanceled();
-
- DfaInstructionState instructionState = queue.remove(0);
- if (LOG.isDebugEnabled()) {
- LOG.debug(instructionState.toString());
- }
- //System.out.println(instructionState.toString());
-
- Instruction instruction = instructionState.getInstruction();
- long distance = instructionState.getDistanceFromStart();
+ for (DfaInstructionState instructionState : queue.getNextInstructionStates(joinInstructions)) {
+ if (count++ % 1024 == 0 && measurer.isTimeOver()) {
+ LOG.debug("Too complex because the analysis took too long");
+ psiBlock.putUserData(TOO_EXPENSIVE_HASH, psiBlock.getText().hashCode());
+ return RunnerResult.TOO_COMPLEX;
+ }
+ ProgressManager.checkCanceled();
- if (instruction instanceof BranchingInstruction) {
- if (!instruction.setMemoryStateProcessed(instructionState.getMemoryState().createCopy())) {
- LOG.debug("Too complex because too many different possible states");
- return RunnerResult.TOO_COMPLEX; // Too complex :(
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(instructionState.toString());
+ }
+ //System.out.println(instructionState.toString());
+
+ Instruction instruction = instructionState.getInstruction();
+
+ if (instruction instanceof BranchingInstruction) {
+ BranchingInstruction branching = (BranchingInstruction)instruction;
+ if (processedStates.get(branching).contains(instructionState.getMemoryState())) {
+ continue;
+ }
+ if (processedStates.get(branching).size() > MAX_STATES_PER_BRANCH) {
+ LOG.debug("Too complex because too many different possible states");
+ return RunnerResult.TOO_COMPLEX; // Too complex :(
+ }
+ processedStates.putValue(branching, instructionState.getMemoryState().createCopy());
}
- }
- DfaInstructionState[] after = acceptInstruction(visitor, instructionState);
- for (DfaInstructionState state : after) {
- Instruction nextInstruction = state.getInstruction();
- if ((!(nextInstruction instanceof BranchingInstruction) || !nextInstruction.isMemoryStateProcessed(state.getMemoryState())) && instruction.getIndex() < endOffset) {
- state.setDistanceFromStart(distance + 1);
- queue.add(state);
+ DfaInstructionState[] after = acceptInstruction(visitor, instructionState);
+ for (DfaInstructionState state : after) {
+ Instruction nextInstruction = state.getInstruction();
+ if (nextInstruction.getIndex() >= endOffset) {
+ continue;
+ }
+ if (nextInstruction instanceof BranchingInstruction) {
+ BranchingInstruction branching = (BranchingInstruction)nextInstruction;
+ if (processedStates.get(branching).contains(state.getMemoryState()) ||
+ incomingStates.get(branching).contains(state.getMemoryState())) {
+ continue;
+ }
+ incomingStates.putValue(branching, state.getMemoryState().createCopy());
+ }
+ queue.offer(state);
}
}
-
- count++;
}
psiBlock.putUserData(TOO_EXPENSIVE_HASH, null);
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaInstructionState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaInstructionState.java
index 87d8b8a1da50..1683807450ab 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaInstructionState.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaInstructionState.java
@@ -25,23 +25,26 @@
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInspection.dataFlow.instructions.Instruction;
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
-public class DfaInstructionState {
+import java.util.Collections;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+public class DfaInstructionState implements Comparable<DfaInstructionState> {
public static final DfaInstructionState[] EMPTY_ARRAY = new DfaInstructionState[0];
private final DfaMemoryState myBeforeMemoryState;
private final Instruction myInstruction;
- private long myDistanceFromStart = 0;
public DfaInstructionState(@NotNull Instruction myInstruction, @NotNull DfaMemoryState myBeforeMemoryState) {
this.myBeforeMemoryState = myBeforeMemoryState;
this.myInstruction = myInstruction;
}
- public long getDistanceFromStart() { return myDistanceFromStart; }
-
- public void setDistanceFromStart(long distanceFromStart) { myDistanceFromStart = distanceFromStart; }
-
@NotNull
public Instruction getInstruction() {
return myInstruction;
@@ -55,4 +58,58 @@ public class DfaInstructionState {
public String toString() {
return getInstruction().getIndex() + " " + getInstruction() + ": " + getMemoryState().toString();
}
+
+ @Override
+ public int compareTo(@NotNull DfaInstructionState o) {
+ return myInstruction.getIndex() - o.myInstruction.getIndex();
+ }
}
+
+class StateQueue {
+ private final PriorityQueue<DfaInstructionState> myQueue = new PriorityQueue<DfaInstructionState>();
+ private final Set<Pair<Instruction, DfaMemoryState>> mySet = ContainerUtil.newHashSet();
+
+ void offer(DfaInstructionState state) {
+ if (mySet.add(Pair.create(state.getInstruction(), state.getMemoryState()))) {
+ myQueue.offer(state);
+ }
+ }
+
+ boolean isEmpty() {
+ return myQueue.isEmpty();
+ }
+
+ List<DfaInstructionState> getNextInstructionStates(Set<Instruction> joinInstructions) {
+ DfaInstructionState state = myQueue.poll();
+ final Instruction instruction = state.getInstruction();
+ mySet.remove(Pair.create(instruction, state.getMemoryState()));
+
+ DfaInstructionState next = myQueue.peek();
+ if (next == null || next.compareTo(state) != 0) return Collections.singletonList(state);
+
+ List<DfaMemoryStateImpl> memoryStates = ContainerUtil.newArrayList();
+ memoryStates.add((DfaMemoryStateImpl)state.getMemoryState());
+ while (!myQueue.isEmpty() && myQueue.peek().compareTo(state) == 0) {
+ DfaMemoryState anotherState = myQueue.poll().getMemoryState();
+ mySet.remove(Pair.create(instruction, anotherState));
+ memoryStates.add((DfaMemoryStateImpl)anotherState);
+ }
+
+ if (memoryStates.size() > 1 && joinInstructions.contains(instruction)) {
+ while (true) {
+ List<DfaMemoryStateImpl> nextStates = new StateMerger(memoryStates).merge();
+ if (nextStates == null) break;
+ memoryStates = nextStates;
+ }
+ }
+
+ return ContainerUtil.map(memoryStates, new Function<DfaMemoryStateImpl, DfaInstructionState>() {
+ @Override
+ public DfaInstructionState fun(DfaMemoryStateImpl state) {
+ return new DfaInstructionState(instruction, state);
+ }
+ });
+ }
+
+
+} \ No newline at end of file
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java
index 8309670020ab..106267affda4 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java
@@ -59,4 +59,13 @@ public interface DfaMemoryState {
@Nullable
DfaConstValue getConstantValue(DfaVariableValue value);
+
+ /**
+ * Ephemeral means a state that was created when considering a method contract and checking if one of its arguments is null.
+ * With explicit null check, that would result in any non-annotated variable being treated as nullable and producing possible NPE warnings later.
+ * With contracts, we don't want this. So the state where this variable is null is marked ephemeral and no NPE warnings are issued for such states.
+ */
+ void markEphemeral();
+
+ boolean isEphemeral();
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java
index d77a0f985985..f5430d5fd713 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java
@@ -26,6 +26,7 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.util.UnorderedPair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.TypeConversionUtil;
@@ -41,43 +42,50 @@ import java.util.*;
public class DfaMemoryStateImpl implements DfaMemoryState {
private final DfaValueFactory myFactory;
- private final List<SortedIntSet> myEqClasses = new ArrayList<SortedIntSet>();
- private final Stack<DfaValue> myStack = new Stack<DfaValue>();
- private TIntStack myOffsetStack = new TIntStack(1);
- private final TLongHashSet myDistinctClasses = new TLongHashSet();
- private final THashMap<DfaVariableValue,DfaVariableState> myVariableStates = new THashMap<DfaVariableValue, DfaVariableState>();
- private final THashSet<DfaVariableValue> myUnknownVariables = new THashSet<DfaVariableValue>();
+ private final List<EqClass> myEqClasses;
+ private final Stack<DfaValue> myStack;
+ private TIntStack myOffsetStack;
+ private final TLongHashSet myDistinctClasses;
+ private final Map<DfaVariableValue,DfaVariableState> myVariableStates;
+ private final Map<DfaVariableValue,DfaVariableState> myDefaultVariableStates;
+ private final Set<DfaVariableValue> myUnknownVariables;
+ private boolean myEphemeral;
public DfaMemoryStateImpl(final DfaValueFactory factory) {
myFactory = factory;
+ myDefaultVariableStates = ContainerUtil.newTroveMap();
+ myEqClasses = ContainerUtil.newArrayList();
+ myUnknownVariables = ContainerUtil.newTroveSet();
+ myVariableStates = ContainerUtil.newTroveMap();
+ myDistinctClasses = new TLongHashSet();
+ myOffsetStack = new TIntStack();
+ myStack = new Stack<DfaValue>();
+ }
+
+ protected DfaMemoryStateImpl(DfaMemoryStateImpl toCopy) {
+ myFactory = toCopy.myFactory;
+ myEphemeral = toCopy.myEphemeral;
+ myDefaultVariableStates = toCopy.myDefaultVariableStates; // shared between all states
+
+ myStack = new Stack<DfaValue>(toCopy.myStack);
+ myDistinctClasses = new TLongHashSet(toCopy.myDistinctClasses.toArray());
+ myUnknownVariables = new THashSet<DfaVariableValue>(toCopy.myUnknownVariables);
+ myOffsetStack = toCopy.myOffsetStack;
+
+ myEqClasses = ContainerUtil.newArrayList(toCopy.myEqClasses);
+ myVariableStates = new THashMap<DfaVariableValue, DfaVariableState>(toCopy.myVariableStates);
+
+ myCachedDistinctClassPairs = toCopy.myCachedDistinctClassPairs;
+ myCachedNonTrivialEqClasses = toCopy.myCachedNonTrivialEqClasses;
}
public DfaValueFactory getFactory() {
return myFactory;
}
- protected DfaMemoryStateImpl createNew() {
- return new DfaMemoryStateImpl(myFactory);
- }
-
@Override
public DfaMemoryStateImpl createCopy() {
- DfaMemoryStateImpl newState = createNew();
-
- newState.myStack.addAll(myStack);
- newState.myDistinctClasses.addAll(myDistinctClasses.toArray());
- newState.myUnknownVariables.addAll(myUnknownVariables);
- newState.myOffsetStack = new TIntStack(myOffsetStack);
-
- for (int i = 0; i < myEqClasses.size(); i++) {
- SortedIntSet aClass = myEqClasses.get(i);
- newState.myEqClasses.add(aClass != null ? new SortedIntSet(aClass.toNativeArray()) : null);
- }
-
- for (DfaVariableValue dfaVariableValue : myVariableStates.keySet()) {
- newState.myVariableStates.put(dfaVariableValue, myVariableStates.get(dfaVariableValue).clone());
- }
- return newState;
+ return new DfaMemoryStateImpl(this);
}
public boolean equals(Object obj) {
@@ -85,84 +93,70 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (!(obj instanceof DfaMemoryStateImpl)) return false;
DfaMemoryStateImpl that = (DfaMemoryStateImpl)obj;
- if (myDistinctClasses.size() != that.myDistinctClasses.size()) return false;
- if (myStack.size() != that.myStack.size()) return false;
- if (myOffsetStack.size() != that.myOffsetStack.size()) return false;
- if (myVariableStates.size() != that.myVariableStates.size()) return false;
- if (myUnknownVariables.size() != that.myUnknownVariables.size()) return false;
+ return equalsSuperficially(that) && myUnknownVariables.equals(that.myUnknownVariables) && equalsByRelations(that);
+ }
- if (!myStack.equals(that.myStack)) return false;
- if (!myOffsetStack.equals(that.myOffsetStack)) return false;
- if (!myVariableStates.equals(that.myVariableStates)) return false;
- if (!myUnknownVariables.equals(that.myUnknownVariables)) return false;
-
+ boolean equalsSuperficially(DfaMemoryStateImpl other) {
+ return myEphemeral == other.myEphemeral && myStack.equals(other.myStack) && myOffsetStack.equals(other.myOffsetStack);
+ }
+
+ boolean equalsByRelations(DfaMemoryStateImpl that) {
+ if (myDistinctClasses.size() != that.myDistinctClasses.size()) return false;
if (!getNonTrivialEqClasses().equals(that.getNonTrivialEqClasses())) return false;
if (!getDistinctClassPairs().equals(that.getDistinctClassPairs())) return false;
-
+ if (!myVariableStates.equals(that.myVariableStates)) return false;
return true;
}
- private Set<Set<SortedIntSet>> getDistinctClassPairs() {
- Set<Set<SortedIntSet>> result = ContainerUtil.newHashSet();
+ private Set<UnorderedPair<EqClass>> myCachedDistinctClassPairs;
+ Set<UnorderedPair<EqClass>> getDistinctClassPairs() {
+ if (myCachedDistinctClassPairs != null) return myCachedDistinctClassPairs;
+
+ Set<UnorderedPair<EqClass>> result = ContainerUtil.newHashSet();
for (long encodedPair : myDistinctClasses.toArray()) {
- THashSet<SortedIntSet> pair = new THashSet<SortedIntSet>(2);
- pair.add(myEqClasses.get(low(encodedPair)));
- pair.add(myEqClasses.get(high(encodedPair)));
- result.add(pair);
+ result.add(new UnorderedPair<EqClass>(myEqClasses.get(low(encodedPair)), myEqClasses.get(high(encodedPair))));
}
- return result;
+ return myCachedDistinctClassPairs = result;
}
- private Set<SortedIntSet> getNonTrivialEqClasses() {
- Set<SortedIntSet> result = ContainerUtil.newHashSet();
- for (SortedIntSet eqClass : myEqClasses) {
+ private Set<EqClass> myCachedNonTrivialEqClasses;
+ Set<EqClass> getNonTrivialEqClasses() {
+ if (myCachedNonTrivialEqClasses != null) return myCachedNonTrivialEqClasses;
+
+ Set<EqClass> result = ContainerUtil.newHashSet();
+ for (EqClass eqClass : myEqClasses) {
if (eqClass != null && eqClass.size() > 1) {
result.add(eqClass);
}
}
- return result;
+ return myCachedNonTrivialEqClasses = result;
}
public int hashCode() {
- return 0;
- //return ((myEqClasses.hashCode() * 31 + myStack.hashCode()) * 31 + myVariableStates.hashCode()) * 31 + myUnknownVariables.hashCode();
- }
-
- private void appendClass(StringBuilder buf, @Nullable SortedIntSet aClass) {
- if (aClass == null) return;
-
- buf.append("(");
-
- for (int i = 0; i < aClass.size(); i++) {
- if (i > 0) buf.append(", ");
- int value = aClass.get(i);
- DfaValue dfaValue = myFactory.getValue(value);
- buf.append(dfaValue);
- }
- buf.append(")");
+ return (((getNonTrivialEqClasses().hashCode() * 31 +
+ getDistinctClassPairs().hashCode()) * 31 +
+ myStack.hashCode()) * 31 +
+ myUnknownVariables.hashCode()) * 31 +
+ myVariableStates.hashCode();
}
@SuppressWarnings({"HardCodedStringLiteral"})
public String toString() {
StringBuilder result = new StringBuilder();
result.append('<');
+ if (myEphemeral) {
+ result.append("ephemeral, ");
+ }
- for (SortedIntSet set : getNonTrivialEqClasses()) {
- appendClass(result, set);
+ for (EqClass set : getNonTrivialEqClasses()) {
+ result.append(set);
}
if (!myDistinctClasses.isEmpty()) {
result.append("\n distincts: ");
List<String> distincts = new ArrayList<String>();
- for (Set<SortedIntSet> pair : getDistinctClassPairs()) {
- ArrayList<SortedIntSet> list = new ArrayList<SortedIntSet>(pair);
- StringBuilder one = new StringBuilder();
- one.append("{");
- appendClass(one, list.get(0));
- one.append(", ");
- appendClass(one, list.get(1));
- one.append("}");
- distincts.add(one.toString());
+ for (UnorderedPair<EqClass> pair : getDistinctClassPairs()) {
+ distincts.add("{" + pair.first + ", " + pair.second + "}");
}
Collections.sort(distincts);
result.append(StringUtil.join(distincts, " "));
@@ -174,7 +168,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (!myVariableStates.isEmpty()) {
result.append("\n vars: ");
for (Map.Entry<DfaVariableValue, DfaVariableState> entry : myVariableStates.entrySet()) {
- result.append("\n[").append(entry.getKey()).append("->").append(entry.getValue()).append("]");
+ result.append("[").append(entry.getKey()).append("->").append(entry.getValue()).append("] ");
}
}
if (!myUnknownVariables.isEmpty()) {
@@ -201,11 +195,13 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
@Override
public int popOffset() {
+ myOffsetStack = new TIntStack(myOffsetStack);
return myOffsetStack.pop();
}
@Override
public void pushOffset(int offset) {
+ myOffsetStack = new TIntStack(myOffsetStack);
myOffsetStack.push(offset);
}
@@ -220,13 +216,13 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
flushVariable(var);
if (value instanceof DfaUnknownValue) {
- getVariableState(var).setNullable(false);
+ setVariableState(var, getVariableState(var).withNullable(false));
return;
}
- getVariableState(var).setValue(value);
+ setVariableState(var, getVariableState(var).withValue(value));
if (value instanceof DfaTypeValue) {
- getVariableState(var).setNullable(((DfaTypeValue)value).isNullable());
+ setVariableState(var, getVariableState(var).withNullable(((DfaTypeValue)value).isNullable()));
DfaRelationValue dfaInstanceof = myFactory.getRelationFactory().createRelation(var, value, JavaTokenType.INSTANCEOF_KEYWORD, false);
if (((DfaTypeValue)value).isNotNull()) {
applyCondition(dfaInstanceof);
@@ -240,10 +236,10 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
applyCondition(dfaEqual);
if (value instanceof DfaVariableValue) {
- myVariableStates.put(var, getVariableState((DfaVariableValue)value).clone());
+ setVariableState(var, getVariableState((DfaVariableValue)value));
}
else if (value instanceof DfaBoxedValue) {
- getVariableState(var).setNullable(false);
+ setVariableState(var, getVariableState(var).withNullable(false));
applyCondition(compareToNull(var, true));
}
}
@@ -260,7 +256,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (!canBeReused(dfaValue) && !(((DfaBoxedValue)dfaValue).getWrappedValue() instanceof DfaConstValue)) {
return null;
}
- SortedIntSet aClass = new SortedIntSet();
+ EqClass aClass = new EqClass(myFactory);
aClass.add(dfaValue.getID());
myEqClasses.add(aClass);
@@ -270,20 +266,11 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
@NotNull
private List<DfaValue> getEqClassesFor(@NotNull DfaValue dfaValue) {
int index = getEqClassIndex(dfaValue);
- SortedIntSet set = index == -1 ? null : myEqClasses.get(index);
+ EqClass set = index == -1 ? null : myEqClasses.get(index);
if (set == null) {
return Collections.emptyList();
}
- final List<DfaValue> result = new ArrayList<DfaValue>(set.size());
- set.forEach(new TIntProcedure() {
- @Override
- public boolean execute(int c1) {
- DfaValue value = myFactory.getValue(c1);
- result.add(value);
- return true;
- }
- });
- return result;
+ return set.getMemberValues();
}
private boolean canBeNaN(@NotNull DfaValue dfaValue) {
@@ -309,7 +296,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
private int getEqClassIndex(@NotNull DfaValue dfaValue) {
for (int i = 0; i < myEqClasses.size(); i++) {
- SortedIntSet aClass = myEqClasses.get(i);
+ EqClass aClass = myEqClasses.get(i);
if (aClass != null && aClass.contains(dfaValue.getID())) {
if (!canBeReused(dfaValue) && aClass.size() > 1) return -1;
return i;
@@ -368,8 +355,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
}
private boolean uniteClasses(int c1Index, int c2Index) {
- SortedIntSet c1 = myEqClasses.get(c1Index);
- SortedIntSet c2 = myEqClasses.get(c2Index);
+ EqClass c1 = myEqClasses.get(c1Index);
+ EqClass c2 = myEqClasses.get(c2Index);
Set<DfaVariableValue> vars = ContainerUtil.newTroveSet();
Set<DfaVariableValue> negatedVars = ContainerUtil.newTroveSet();
@@ -379,15 +366,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
int nConst = 0;
for (int c : cs) {
- DfaValue dfaValue = myFactory.getValue(c);
- if (dfaValue instanceof DfaBoxedValue) dfaValue = ((DfaBoxedValue)dfaValue).getWrappedValue();
- if (dfaValue instanceof DfaUnboxedValue) dfaValue = ((DfaUnboxedValue)dfaValue).getVariable();
+ DfaValue dfaValue = unwrap(myFactory.getValue(c));
if (dfaValue instanceof DfaConstValue) nConst++;
if (dfaValue instanceof DfaVariableValue) {
DfaVariableValue variableValue = (DfaVariableValue)dfaValue;
if (variableValue.isNegated()) {
negatedVars.add(variableValue.createNegated());
- } else {
+ }
+ else {
vars.add(variableValue);
}
}
@@ -412,6 +398,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
}
}
+ myEqClasses.set(c1Index, c1 = new EqClass(c1));
for (int i = 0; i < c2.size(); i++) {
int c = c2.get(i);
c1.add(c);
@@ -458,7 +445,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
public boolean isNull(DfaValue dfaValue) {
if (dfaValue instanceof DfaTypeValue && ((DfaTypeValue)dfaValue).isNotNull()) return false;
- if (dfaValue instanceof DfaConstValue) return ((DfaConstValue)dfaValue).getConstant() == null;
+ if (dfaValue instanceof DfaConstValue) return ((DfaConstValue)dfaValue).getValue() == null;
if (dfaValue instanceof DfaVariableValue) {
int c1Index = getEqClassIndex(dfaValue);
@@ -495,33 +482,47 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
@Override
@Nullable
public DfaConstValue getConstantValue(DfaVariableValue value) {
- DfaConstValue result = null;
- for (DfaValue equal : getEqClassesFor(value)) {
- if (equal instanceof DfaVariableValue) continue;
- DfaConstValue constValue = asConstantValue(equal);
- if (constValue == null) return null;
- result = constValue;
- }
- return result;
+ int index = getEqClassIndex(value);
+ EqClass ec = index == -1 ? null : myEqClasses.get(index);
+ return ec == null ? null : ec.findConstant(true);
+ }
+
+ @Override
+ public void markEphemeral() {
+ myEphemeral = true;
+ }
+
+ @Override
+ public boolean isEphemeral() {
+ return myEphemeral;
}
@Override
public boolean applyInstanceofOrNull(DfaRelationValue dfaCond) {
- DfaValue left = dfaCond.getLeftOperand();
- if (left instanceof DfaBoxedValue) {
- left = ((DfaBoxedValue)left).getWrappedValue();
- }
- else if (left instanceof DfaUnboxedValue) {
- left = ((DfaUnboxedValue)left).getVariable();
- }
+ DfaValue left = unwrap(dfaCond.getLeftOperand());
if (!(left instanceof DfaVariableValue)) return true;
DfaVariableValue dfaVar = (DfaVariableValue)left;
DfaTypeValue dfaType = (DfaTypeValue)dfaCond.getRightOperand();
- final DfaVariableState varState = getVariableState(dfaVar);
- return isNull(dfaVar) || varState.setInstanceofValue(dfaType);
+ if (isUnknownState(dfaVar) || isNull(dfaVar)) return true;
+ DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue(dfaType);
+ if (newState != null) {
+ setVariableState(dfaVar, newState);
+ return true;
+ }
+ return false;
+ }
+
+ static DfaValue unwrap(DfaValue value) {
+ if (value instanceof DfaBoxedValue) {
+ return ((DfaBoxedValue)value).getWrappedValue();
+ }
+ else if (value instanceof DfaUnboxedValue) {
+ return ((DfaUnboxedValue)value).getVariable();
+ }
+ return value;
}
@Override
@@ -563,12 +564,25 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (dfaRight instanceof DfaTypeValue) {
if (dfaLeft instanceof DfaVariableValue) {
- DfaVariableState varState = getVariableState((DfaVariableValue)dfaLeft);
DfaVariableValue dfaVar = (DfaVariableValue)dfaLeft;
+ if (isUnknownState(dfaVar)) return true;
+
if (isNegated) {
- return varState.addNotInstanceofValue((DfaTypeValue)dfaRight) || applyCondition(compareToNull(dfaVar, false));
+ DfaVariableState newState = getVariableState(dfaVar).withNotInstanceofValue((DfaTypeValue)dfaRight);
+ if (newState != null) {
+ setVariableState(dfaVar, newState);
+ return true;
+ }
+ return applyCondition(compareToNull(dfaVar, false));
+ }
+ if (applyCondition(compareToNull(dfaVar, true))) {
+ DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue((DfaTypeValue)dfaRight);
+ if (newState != null) {
+ setVariableState(dfaVar, newState);
+ return true;
+ }
}
- return applyCondition(compareToNull(dfaVar, true)) && varState.setInstanceofValue((DfaTypeValue)dfaRight);
+ return false;
}
return true;
}
@@ -595,7 +609,9 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (isNotNull(dfaVar)) {
return true;
}
- getVariableState(dfaVar).setNullable(true);
+ if (!isUnknownState(dfaVar)) {
+ setVariableState(dfaVar, getVariableState(dfaVar).withNullability(Nullness.NULLABLE));
+ }
}
return false;
}
@@ -637,7 +653,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (!TypeConversionUtil.isPrimitiveWrapper(type)) {
return true;
}
- if (negated && !isMaybeBoxedConstant(dfaRight)) {
+ if (negated && !(unwrap(dfaRight) instanceof DfaConstValue)) {
// from the fact (wrappers are not the same) does not follow (unboxed values are not equals)
return true;
}
@@ -646,11 +662,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
return applyRelation(boxedFactory.createUnboxed(dfaLeft), boxedFactory.createUnboxed(dfaRight), negated);
}
- private static boolean isMaybeBoxedConstant(DfaValue val) {
- return val instanceof DfaConstValue ||
- val instanceof DfaBoxedValue && ((DfaBoxedValue)val).getWrappedValue() instanceof DfaConstValue;
- }
-
private boolean checkCompareWithBooleanLiteral(DfaValue dfaLeft, DfaValue dfaRight, boolean negated) {
if (dfaRight instanceof DfaConstValue) {
Object constVal = ((DfaConstValue)dfaRight).getValue();
@@ -691,18 +702,28 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (!isNegated) { //Equals
if (c1Index.equals(c2Index)) return true;
if (!uniteClasses(c1Index, c2Index)) return false;
+
+ for (long encodedPair : myDistinctClasses.toArray()) {
+ EqClass c1 = myEqClasses.get(low(encodedPair));
+ EqClass c2 = myEqClasses.get(high(encodedPair));
+ if (c1.findConstant(false) != null && c2.findConstant(false) != null) {
+ myDistinctClasses.remove(encodedPair);
+ }
+ }
+ myCachedDistinctClassPairs = null;
+ myCachedNonTrivialEqClasses = null;
}
else { // Not Equals
if (c1Index.equals(c2Index)) return false;
makeClassesDistinct(c1Index, c2Index);
+ myCachedDistinctClassPairs = null;
}
return true;
}
private boolean isUnknownState(DfaValue val) {
- if (val instanceof DfaBoxedValue) return isUnknownState(((DfaBoxedValue)val).getWrappedValue());
- if (val instanceof DfaUnboxedValue) return isUnknownState(((DfaUnboxedValue)val).getVariable());
+ val = unwrap(val);
if (val instanceof DfaVariableValue) {
if (myUnknownVariables.contains(val)) return true;
DfaVariableValue negatedValue = ((DfaVariableValue)val).getNegatedValue();
@@ -731,17 +752,33 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
return myFactory.getRelationFactory().createRelation(dfaVar, dfaNull, JavaTokenType.EQEQ, negated);
}
+ void setVariableState(DfaVariableValue dfaVar, DfaVariableState state) {
+ assert !myUnknownVariables.contains(dfaVar);
+ if (state.equals(myDefaultVariableStates.get(dfaVar))) {
+ myVariableStates.remove(dfaVar);
+ } else {
+ myVariableStates.put(dfaVar, state);
+ }
+ }
+
public DfaVariableState getVariableState(DfaVariableValue dfaVar) {
DfaVariableState state = myVariableStates.get(dfaVar);
if (state == null) {
- state = createVariableState(dfaVar);
+ state = myDefaultVariableStates.get(dfaVar);
+ if (state == null) {
+ state = createVariableState(dfaVar);
+ DfaTypeValue initialType = dfaVar.getTypeValue();
+ if (initialType != null) {
+ state = state.withInstanceofValue(initialType);
+ assert state != null;
+ }
+ myDefaultVariableStates.put(dfaVar, state);
+ }
+
if (isUnknownState(dfaVar)) {
- state.setNullable(false);
- return state;
+ return state.withNullable(false);
}
-
- myVariableStates.put(dfaVar, state);
}
return state;
@@ -757,28 +794,25 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
@Override
public void flushFields(DfaVariableValue[] fields) {
- Set<DfaVariableValue> allVars = new HashSet<DfaVariableValue>(myVariableStates.keySet());
- Collections.addAll(allVars, fields);
-
- Set<DfaVariableValue> dependencies = new HashSet<DfaVariableValue>();
- for (DfaVariableValue variableValue : allVars) {
- dependencies.addAll(myFactory.getVarFactory().getAllQualifiedBy(variableValue));
- }
- allVars.addAll(dependencies);
-
- for (DfaVariableValue value : allVars) {
- if (myVariableStates.containsKey(value) || getEqClassIndex(value) >= 0) {
- if (value.isFlushableByCalls()) {
- doFlush(value);
- myUnknownVariables.add(value);
+ for (EqClass aClass : myEqClasses) {
+ if (aClass != null) {
+ for (DfaVariableValue value : aClass.getVariables()) {
+ if (value.isFlushableByCalls()) {
+ doFlush(value, true);
+ }
}
}
}
+ for (DfaVariableValue value : new ArrayList<DfaVariableValue>(myVariableStates.keySet())) {
+ if (value.isFlushableByCalls()) {
+ doFlush(value, true);
+ }
+ }
}
@Override
public void flushVariable(@NotNull DfaVariableValue variable) {
- doFlush(variable);
+ doFlush(variable, false);
flushDependencies(variable);
myUnknownVariables.remove(variable);
myUnknownVariables.removeAll(myFactory.getVarFactory().getAllQualifiedBy(variable));
@@ -786,11 +820,15 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
public void flushDependencies(DfaVariableValue variable) {
for (DfaVariableValue dependent : myFactory.getVarFactory().getAllQualifiedBy(variable)) {
- doFlush(dependent);
+ doFlush(dependent, false);
}
}
- private void doFlush(DfaVariableValue varPlain) {
+ Set<DfaVariableValue> getUnknownVariables() {
+ return myUnknownVariables;
+ }
+
+ void doFlush(DfaVariableValue varPlain, boolean markUnknown) {
DfaVariableValue varNegated = varPlain.getNegatedValue();
final int idPlain = varPlain.getID();
@@ -799,7 +837,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
int size = myEqClasses.size();
int interruptCount = 0;
for (int varClassIndex = 0; varClassIndex < size; varClassIndex++) {
- final SortedIntSet varClass = myEqClasses.get(varClassIndex);
+ EqClass varClass = myEqClasses.get(varClassIndex);
if (varClass == null) continue;
for (int i = 0; i < varClass.size(); i++) {
@@ -809,6 +847,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
int cl = varClass.get(i);
DfaValue value = myFactory.getValue(cl);
if (mine(idPlain, value) || idNegated >= 0 && mine(idNegated, value)) {
+ myEqClasses.set(varClassIndex, varClass = new EqClass(varClass));
varClass.remove(i);
break;
}
@@ -823,10 +862,10 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
}
}
}
- else if (containsConstantsOnly(varClassIndex)) {
+ else if (varClass.containsConstantsOnly()) {
for (long pair : myDistinctClasses.toArray()) {
- if (low(pair) == varClassIndex && containsConstantsOnly(high(pair)) ||
- high(pair) == varClassIndex && containsConstantsOnly(low(pair))) {
+ if (low(pair) == varClassIndex && myEqClasses.get(high(pair)).containsConstantsOnly() ||
+ high(pair) == varClassIndex && myEqClasses.get(low(pair)).containsConstantsOnly()) {
myDistinctClasses.remove(pair);
}
}
@@ -837,28 +876,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (varNegated != null) {
myVariableStates.remove(varNegated);
}
- }
-
- @Nullable private static DfaConstValue asConstantValue(DfaValue value) {
- if (value instanceof DfaConstValue) return (DfaConstValue)value;
- if (value instanceof DfaBoxedValue && ((DfaBoxedValue)value).getWrappedValue() instanceof DfaConstValue) return (DfaConstValue)((DfaBoxedValue)value).getWrappedValue();
- return null;
- }
-
- private boolean containsConstantsOnly(int id) {
- SortedIntSet varClass = myEqClasses.get(id);
- for (int i = 0; i < varClass.size(); i++) {
- if (asConstantValue(myFactory.getValue(varClass.get(i))) == null) {
- return false;
- }
+ if (markUnknown) {
+ myUnknownVariables.add(varPlain);
}
-
- return true;
+ myCachedNonTrivialEqClasses = null;
+ myCachedDistinctClassPairs = null;
}
private static boolean mine(int id, DfaValue value) {
- return value != null && id == value.getID() ||
- value instanceof DfaBoxedValue && ((DfaBoxedValue)value).getWrappedValue().getID() == id ||
- value instanceof DfaUnboxedValue && ((DfaUnboxedValue)value).getVariable().getID() == id;
+ return value != null && id == unwrap(value).getID();
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java
index 3b493d4dea26..8ed7836f0dfd 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java
@@ -37,9 +37,6 @@ import java.util.List;
import java.util.Set;
public class DfaPsiUtil {
- public static boolean isPlainMutableField(PsiVariable var) {
- return !var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && !var.hasModifierProperty(PsiModifier.VOLATILE) && var instanceof PsiField;
- }
public static boolean isFinalField(PsiVariable var) {
return var.hasModifierProperty(PsiModifier.FINAL) && !var.hasModifierProperty(PsiModifier.TRANSIENT) && var instanceof PsiField;
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java
index 8c3cce8a51ea..16a2a6d8e3a7 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java
@@ -163,9 +163,10 @@ public class DfaUtil {
}
@Override
- public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
+ public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState _memState) {
final Instruction nextInstruction = runner.getInstruction(instruction.getIndex() + 1);
+ ValuableDataFlowRunner.MyDfaMemoryState memState = (ValuableDataFlowRunner.MyDfaMemoryState)_memState;
final DfaValue dfaSource = memState.pop();
final DfaValue dfaDest = memState.pop();
@@ -176,10 +177,10 @@ public class DfaUtil {
final IElementType type = parent instanceof PsiAssignmentExpression
? ((PsiAssignmentExpression)parent).getOperationTokenType() : JavaTokenType.EQ;
// store current value - to use in case of '+='
- final PsiExpression prevValue = ((ValuableDataFlowRunner.ValuableDfaVariableState)((ValuableDataFlowRunner.MyDfaMemoryState)memState).getVariableState(var)).myExpression;
+ final PsiExpression prevValue = ((ValuableDataFlowRunner.ValuableDfaVariableState)memState.getVariableState(var)).myExpression;
memState.setVarValue(var, dfaSource);
// state may have been changed so re-retrieve it
- final ValuableDataFlowRunner.ValuableDfaVariableState curState = (ValuableDataFlowRunner.ValuableDfaVariableState)((ValuableDataFlowRunner.MyDfaMemoryState)memState).getVariableState(var);
+ final ValuableDataFlowRunner.ValuableDfaVariableState curState = (ValuableDataFlowRunner.ValuableDfaVariableState)memState.getVariableState(var);
final PsiExpression curValue = curState.myExpression;
final PsiExpression nextValue;
if (type == JavaTokenType.PLUSEQ && prevValue != null) {
@@ -196,7 +197,7 @@ public class DfaUtil {
else {
nextValue = curValue == null ? rightValue : curValue;
}
- curState.myExpression = nextValue;
+ memState.setVariableState(var, curState.withExpression(nextValue));
}
memState.push(dfaDest);
return new DfaInstructionState[]{new DfaInstructionState(nextInstruction, memState)};
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java
index 81738226061a..61d61c26cf79 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java
@@ -24,138 +24,134 @@
*/
package com.intellij.codeInspection.dataFlow;
+import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
import com.intellij.codeInspection.dataFlow.value.DfaTypeValue;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
-import com.intellij.psi.*;
-import gnu.trove.THashSet;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiPrimitiveType;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.Set;
-public class DfaVariableState implements Cloneable {
- private final Set<DfaTypeValue> myInstanceofValues;
- private final Set<DfaTypeValue> myNotInstanceofValues;
- private Nullness myNullability;
+public class DfaVariableState {
+ protected final Set<DfaPsiType> myInstanceofValues;
+ protected final Set<DfaPsiType> myNotInstanceofValues;
+ protected final Nullness myNullability;
public DfaVariableState(@NotNull DfaVariableValue dfaVar) {
- myInstanceofValues = new HashSet<DfaTypeValue>();
- myNotInstanceofValues = new HashSet<DfaTypeValue>();
-
- myNullability = dfaVar.getInherentNullability();
- DfaTypeValue initialType = dfaVar.getTypeValue();
- if (initialType != null) {
- setInstanceofValue(initialType);
- }
+ this(Collections.<DfaPsiType>emptySet(), Collections.<DfaPsiType>emptySet(), dfaVar.getInherentNullability());
}
- protected DfaVariableState(final DfaVariableState toClone) {
- myInstanceofValues = new THashSet<DfaTypeValue>(toClone.myInstanceofValues);
- myNotInstanceofValues = new THashSet<DfaTypeValue>(toClone.myNotInstanceofValues);
- myNullability = toClone.myNullability;
+ protected DfaVariableState(Set<DfaPsiType> instanceofValues,
+ Set<DfaPsiType> notInstanceofValues, Nullness nullability) {
+ myInstanceofValues = instanceofValues;
+ myNotInstanceofValues = notInstanceofValues;
+ myNullability = nullability;
}
public boolean isNullable() {
return myNullability == Nullness.NULLABLE;
}
- private boolean checkInstanceofValue(DfaTypeValue dfaType) {
+ private boolean checkInstanceofValue(DfaPsiType dfaType) {
if (myInstanceofValues.contains(dfaType)) return true;
- for (DfaTypeValue dfaTypeValue : myNotInstanceofValues) {
+ for (DfaPsiType dfaTypeValue : myNotInstanceofValues) {
if (dfaTypeValue.isAssignableFrom(dfaType)) return false;
}
- for (DfaTypeValue dfaTypeValue : myInstanceofValues) {
+ for (DfaPsiType dfaTypeValue : myInstanceofValues) {
if (!dfaType.isConvertibleFrom(dfaTypeValue)) return false;
}
return true;
}
- public boolean setInstanceofValue(DfaTypeValue dfaType) {
- if (dfaType.isNullable()) {
- myNullability = Nullness.NULLABLE;
- }
-
- if (dfaType.getType() instanceof PsiPrimitiveType) return true;
-
- if (checkInstanceofValue(dfaType)) {
- myInstanceofValues.add(dfaType);
- return true;
+ @Nullable
+ public DfaVariableState withInstanceofValue(DfaTypeValue dfaType) {
+ if (dfaType.getDfaType().getPsiType() instanceof PsiPrimitiveType) return this;
+
+ if (checkInstanceofValue(dfaType.getDfaType())) {
+ DfaVariableState result = dfaType.isNullable() ? withNullability(Nullness.NULLABLE) : this;
+ if (!myInstanceofValues.contains(dfaType.getDfaType())) {
+ HashSet<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
+ newInstanceof.add(dfaType.getDfaType());
+ result = createCopy(newInstanceof, myNotInstanceofValues, result.myNullability);
+ }
+ return result;
}
- return false;
+ return null;
}
- public boolean addNotInstanceofValue(DfaTypeValue dfaType) {
- if (myNotInstanceofValues.contains(dfaType)) return true;
+ @Nullable
+ public DfaVariableState withNotInstanceofValue(DfaTypeValue dfaType) {
+ if (myNotInstanceofValues.contains(dfaType.getDfaType())) return this;
- for (DfaTypeValue dfaTypeValue : myInstanceofValues) {
- if (dfaType.isAssignableFrom(dfaTypeValue)) return false;
+ for (DfaPsiType dfaTypeValue : myInstanceofValues) {
+ if (dfaType.getDfaType().isAssignableFrom(dfaTypeValue)) return null;
}
- myNotInstanceofValues.add(dfaType);
- return true;
+ HashSet<DfaPsiType> newNotInstanceof = ContainerUtil.newHashSet(myNotInstanceofValues);
+ newNotInstanceof.add(dfaType.getDfaType());
+ return createCopy(myInstanceofValues, newNotInstanceof, myNullability);
}
public int hashCode() {
- return myInstanceofValues.hashCode() + myNotInstanceofValues.hashCode();
+ return (myInstanceofValues.hashCode() * 31 + myNotInstanceofValues.hashCode()) * 31 + myNullability.hashCode();
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof DfaVariableState)) return false;
DfaVariableState aState = (DfaVariableState) obj;
- return myInstanceofValues.equals(aState.myInstanceofValues) &&
- myNotInstanceofValues.equals(aState.myNotInstanceofValues) &&
- myNullability == aState.myNullability;
+ return myNullability == aState.myNullability &&
+ myInstanceofValues.equals(aState.myInstanceofValues) &&
+ myNotInstanceofValues.equals(aState.myNotInstanceofValues);
}
- @Override
- protected DfaVariableState clone() {
- return new DfaVariableState(this);
+ protected DfaVariableState createCopy(Set<DfaPsiType> instanceofValues, Set<DfaPsiType> notInstanceofValues, Nullness nullability) {
+ return new DfaVariableState(instanceofValues, notInstanceofValues, nullability);
}
public String toString() {
@NonNls StringBuilder buf = new StringBuilder();
+ buf.append(myNullability);
if (!myInstanceofValues.isEmpty()) {
- buf.append("instanceof ");
- for (Iterator<DfaTypeValue> iterator = myInstanceofValues.iterator(); iterator.hasNext();) {
- DfaTypeValue dfaTypeValue = iterator.next();
- buf.append("{").append(dfaTypeValue).append("}");
- if (iterator.hasNext()) buf.append(", ");
- }
+ buf.append(" instanceof ").append(StringUtil.join(myInstanceofValues, ","));
}
if (!myNotInstanceofValues.isEmpty()) {
- buf.append("not instanceof ");
- for (Iterator<DfaTypeValue> iterator = myNotInstanceofValues.iterator(); iterator.hasNext();) {
- DfaTypeValue dfaTypeValue = iterator.next();
- buf.append("{").append(dfaTypeValue).append("}");
- if (iterator.hasNext()) buf.append(", ");
- }
+ buf.append(" not instanceof ").append(StringUtil.join(myNotInstanceofValues, ","));
}
- buf.append(myNullability);
return buf.toString();
}
+ public Nullness getNullability() {
+ return myNullability;
+ }
+
public boolean isNotNull() {
return myNullability == Nullness.NOT_NULL;
}
- public void setNullable(final boolean nullable) {
- if (myNullability != Nullness.NOT_NULL) {
- myNullability = nullable ? Nullness.NULLABLE : Nullness.UNKNOWN;
- }
+ DfaVariableState withNullability(@NotNull Nullness nullness) {
+ return myNullability == nullness ? this : createCopy(myInstanceofValues, myNotInstanceofValues, nullness);
+ }
+
+ public DfaVariableState withNullable(final boolean nullable) {
+ return myNullability != Nullness.NOT_NULL ? withNullability(nullable ? Nullness.NULLABLE : Nullness.UNKNOWN) : this;
}
- public void setValue(DfaValue value) {
+ public DfaVariableState withValue(DfaValue value) {
+ return this;
}
@Nullable
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/EqClass.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/EqClass.java
new file mode 100644
index 000000000000..c6fc62170ef2
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/EqClass.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInspection.dataFlow;
+
+import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
+import com.intellij.codeInspection.dataFlow.value.DfaValue;
+import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
+import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.TIntProcedure;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author peter
+ */
+class EqClass extends SortedIntSet {
+ private final DfaValueFactory myFactory;
+
+ EqClass(DfaValueFactory factory) {
+ myFactory = factory;
+ }
+
+ EqClass(EqClass toCopy) {
+ super(toCopy.toNativeArray());
+ myFactory = toCopy.myFactory;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("(");
+ for (int i = 0; i < size(); i++) {
+ if (i > 0) buf.append(", ");
+ int value = get(i);
+ DfaValue dfaValue = myFactory.getValue(value);
+ buf.append(dfaValue);
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+ List<DfaVariableValue> getVariables() {
+ List<DfaVariableValue> vars = ContainerUtil.newArrayList();
+ for (DfaValue value : getMemberValues()) {
+ value = DfaMemoryStateImpl.unwrap(value);
+ if (value instanceof DfaVariableValue) {
+ vars.add((DfaVariableValue)value);
+ }
+ }
+ return vars;
+ }
+
+ List<DfaValue> getMemberValues() {
+ final List<DfaValue> result = new ArrayList<DfaValue>(size());
+ forEach(new TIntProcedure() {
+ @Override
+ public boolean execute(int c1) {
+ DfaValue value = myFactory.getValue(c1);
+ result.add(value);
+ return true;
+ }
+ });
+ return result;
+ }
+
+ @Nullable
+ DfaConstValue findConstant(boolean wrapped) {
+ for (DfaValue value : getMemberValues()) {
+ if (wrapped) {
+ value = DfaMemoryStateImpl.unwrap(value);
+ }
+ if (value instanceof DfaConstValue) {
+ return (DfaConstValue)value;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private static DfaConstValue asConstantValue(DfaValue value) {
+ value = DfaMemoryStateImpl.unwrap(value);
+ return value instanceof DfaConstValue ? (DfaConstValue)value : null;
+ }
+
+ boolean containsConstantsOnly() {
+ for (int i = 0; i < size(); i++) {
+ if (asConstantValue(myFactory.getValue(get(i))) == null) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java
new file mode 100644
index 000000000000..8155149ed1ec
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java
@@ -0,0 +1,14 @@
+package com.intellij.codeInspection.dataFlow;
+
+/**
+ * @author peter
+ */
+public enum NullabilityProblem {
+ callNPE,
+ fieldAccessNPE,
+ unboxingNullable,
+ assigningToNotNull,
+ nullableReturn,
+ passingNullableToNotNullParameter,
+ passingNullableArgumentToNonAnnotatedParameter,
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java
index b7fc6c7dcfc8..2d6ee1b71232 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java
@@ -27,36 +27,23 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.instructions.InstanceofInstruction;
import com.intellij.codeInspection.dataFlow.instructions.Instruction;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
-import com.intellij.psi.*;
-import gnu.trove.THashSet;
+import com.intellij.psi.CommonClassNames;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
public class StandardDataFlowRunner extends DataFlowRunner {
- private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.DataFlowRunner");
-
- private final Set<Instruction> myNPEInstructions = new HashSet<Instruction>();
private final Set<Instruction> myCCEInstructions = new HashSet<Instruction>();
- private final Set<PsiExpression> myNullableArguments = new HashSet<PsiExpression>();
- private final Set<PsiExpression> myNullableArgumentsPassedToNonAnnotatedParam = new HashSet<PsiExpression>();
- private final Set<PsiExpression> myNullableAssignments = new HashSet<PsiExpression>();
- private final Set<PsiReturnStatement> myNullableReturns = new HashSet<PsiReturnStatement>();
- private final boolean mySuggestNullableAnnotations;
private boolean myInNullableMethod = false;
private boolean myInNotNullMethod = false;
private boolean myIsInMethod = false;
- private final Set<PsiExpression> myUnboxedNullables = new THashSet<PsiExpression>();
-
- public StandardDataFlowRunner(boolean suggestNullableAnnotations) {
- mySuggestNullableAnnotations = suggestNullableAnnotations;
- }
-
@Override
protected void prepareAnalysis(@NotNull PsiElement psiBlock, Iterable<DfaMemoryState> initialStates) {
myIsInMethod = psiBlock.getParent() instanceof PsiMethod;
@@ -68,17 +55,7 @@ public class StandardDataFlowRunner extends DataFlowRunner {
myInNotNullMethod = NullableNotNullManager.isNotNull(method);
}
- myNPEInstructions.clear();
myCCEInstructions.clear();
- myNullableArguments.clear();
- myNullableArgumentsPassedToNonAnnotatedParam.clear();
- myNullableAssignments.clear();
- myNullableReturns.clear();
- myUnboxedNullables.clear();
- }
-
- public void onInstructionProducesNPE(Instruction instruction) {
- myNPEInstructions.add(instruction);
}
public void onInstructionProducesCCE(Instruction instruction) {
@@ -89,74 +66,24 @@ public class StandardDataFlowRunner extends DataFlowRunner {
return myCCEInstructions;
}
- @NotNull public Set<Instruction> getNPEInstructions() {
- return myNPEInstructions;
- }
-
- @NotNull public Set<PsiReturnStatement> getNullableReturns() {
- return myNullableReturns;
- }
-
public boolean isInNotNullMethod() {
return myInNotNullMethod;
}
- @NotNull public Set<PsiExpression> getNullableArguments() {
- return myNullableArguments;
- }
-
- public Set<PsiExpression> getNullableArgumentsPassedToNonAnnotatedParam() {
- return myNullableArgumentsPassedToNonAnnotatedParam;
- }
-
- @NotNull public Set<PsiExpression> getNullableAssignments() {
- return myNullableAssignments;
+ public boolean isInNullableMethod() {
+ return myInNullableMethod;
}
- @NotNull public Set<PsiExpression> getUnboxedNullables() {
- return myUnboxedNullables;
- }
-
- public void onUnboxingNullable(@NotNull PsiExpression expression) {
- LOG.assertTrue(expression.isValid());
- if (expression.isPhysical()) {
- myUnboxedNullables.add(expression);
- }
- }
-
- public void onPassingNullParameter(PsiExpression expr) {
- myNullableArguments.add(expr);
- }
-
- public void onPassingNullParameterToNonAnnotated(PsiExpression expr) {
- if (mySuggestNullableAnnotations) {
- myNullableArgumentsPassedToNonAnnotatedParam.add(expr);
- }
- }
-
- public void onAssigningToNotNullableVariable(final PsiExpression expr) {
- myNullableAssignments.add(expr);
- }
-
- public void onNullableReturn(final PsiReturnStatement statement) {
- if (myInNullableMethod || !myIsInMethod) return;
- if (myInNotNullMethod || mySuggestNullableAnnotations) {
- myNullableReturns.add(statement);
- }
+ public boolean isInMethod() {
+ return myIsInMethod;
}
public boolean problemsDetected(StandardInstructionVisitor visitor) {
final Pair<Set<Instruction>, Set<Instruction>> constConditions = getConstConditionalExpressions();
return !constConditions.getFirst().isEmpty()
|| !constConditions.getSecond().isEmpty()
- || !myNPEInstructions.isEmpty()
|| !myCCEInstructions.isEmpty()
- || !getRedundantInstanceofs(this, visitor).isEmpty()
- || !myNullableArguments.isEmpty()
- || !myNullableArgumentsPassedToNonAnnotatedParam.isEmpty()
- || !myNullableAssignments.isEmpty()
- || !myNullableReturns.isEmpty()
- || !myUnboxedNullables.isEmpty();
+ || !getRedundantInstanceofs(this, visitor).isEmpty();
}
@NotNull public static Set<Instruction> getRedundantInstanceofs(final DataFlowRunner runner, StandardInstructionVisitor visitor) {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java
index 3770e49600db..90a64901c424 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java
@@ -115,17 +115,15 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (dfaDest instanceof DfaVariableValue) {
DfaVariableValue var = (DfaVariableValue) dfaDest;
- final PsiModifierListOwner psiVariable = var.getPsiVariable();
- if (DfaPsiUtil.getElementNullability(var.getVariableType(), psiVariable) == Nullness.NOT_NULL) {
- if (!memState.checkNotNullable(dfaSource)) {
- onAssigningToNotNullableVariable(instruction);
- }
+ if (var.getInherentNullability() == Nullness.NOT_NULL) {
+ checkNotNullable(memState, dfaSource, NullabilityProblem.assigningToNotNull, instruction.getRExpression());
}
- if (!(psiVariable instanceof PsiField) || !psiVariable.hasModifierProperty(PsiModifier.VOLATILE)) {
+ final PsiModifierListOwner psi = var.getPsiVariable();
+ if (!(psi instanceof PsiField) || !psi.hasModifierProperty(PsiModifier.VOLATILE)) {
memState.setVarValue(var, dfaSource);
}
- } else if (dfaDest instanceof DfaTypeValue && ((DfaTypeValue)dfaDest).isNotNull() && !memState.checkNotNullable(dfaSource)) {
- onAssigningToNotNullableVariable(instruction);
+ } else if (dfaDest instanceof DfaTypeValue && ((DfaTypeValue)dfaDest).isNotNull()) {
+ checkNotNullable(memState, dfaSource, NullabilityProblem.assigningToNotNull, instruction.getRExpression());
}
memState.push(dfaDest);
@@ -133,27 +131,19 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return nextInstruction(instruction, runner, memState);
}
- protected void onAssigningToNotNullableVariable(AssignInstruction instruction) {}
-
@Override
public DfaInstructionState[] visitCheckReturnValue(CheckReturnValueInstruction instruction,
DataFlowRunner runner,
DfaMemoryState memState) {
final DfaValue retValue = memState.pop();
- if (!memState.checkNotNullable(retValue)) {
- onNullableReturn(instruction);
- }
+ checkNotNullable(memState, retValue, NullabilityProblem.nullableReturn, instruction.getReturn());
return nextInstruction(instruction, runner, memState);
}
- protected void onNullableReturn(CheckReturnValueInstruction instruction) {}
-
@Override
public DfaInstructionState[] visitFieldReference(FieldReferenceInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
final DfaValue qualifier = memState.pop();
- if (!memState.checkNotNullable(qualifier)) {
- onInstructionProducesNPE(instruction);
-
+ if (!checkNotNullable(memState, qualifier, NullabilityProblem.fieldAccessNPE, instruction.getElementToAssert())) {
if (qualifier instanceof DfaVariableValue) {
memState.setVarValue((DfaVariableValue)qualifier, runner.getFactory()
.createTypeValue(((DfaVariableValue)qualifier).getVariableType(), Nullness.NOT_NULL));
@@ -194,7 +184,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
final DfaValueFactory factory = runner.getFactory();
DfaValue dfaExpr = factory.createValue(instruction.getCasted());
if (dfaExpr != null) {
- DfaTypeValue dfaType = factory.getTypeFactory().createTypeValue(instruction.getCastTo());
+ DfaTypeValue dfaType = (DfaTypeValue)factory.createTypeValue(instruction.getCastTo(), Nullness.UNKNOWN);
DfaRelationValue dfaInstanceof = factory.getRelationFactory().createRelation(dfaExpr, dfaType, JavaTokenType.INSTANCEOF_KEYWORD, false);
if (dfaInstanceof != null && !memState.applyInstanceofOrNull(dfaInstanceof)) {
onInstructionProducesCCE(instruction);
@@ -218,23 +208,24 @@ public class StandardInstructionVisitor extends InstructionVisitor {
final DfaValue arg = memState.pop();
PsiExpression expr = args[(args.length - i - 1)];
if (map.get(expr) == Nullness.NOT_NULL) {
- if (!memState.checkNotNullable(arg)) {
- onPassingNullParameter(expr);
+ if (!checkNotNullable(memState, arg, NullabilityProblem.passingNullableToNotNullParameter, expr)) {
if (arg instanceof DfaVariableValue) {
memState.setVarValue((DfaVariableValue)arg, runner.getFactory()
.createTypeValue(((DfaVariableValue)arg).getVariableType(), Nullness.NOT_NULL));
}
}
}
- else if (map.get(expr) == Nullness.UNKNOWN && !memState.checkNotNullable(arg)) {
- onPassingNullParameterToNonAnnotated(runner, expr);
+ else if (map.get(expr) == Nullness.UNKNOWN) {
+ checkNotNullable(memState, arg, NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter, expr);
}
}
@NotNull final DfaValue qualifier = memState.pop();
try {
- if (!memState.checkNotNullable(qualifier)) {
- onInstructionProducesNPE(instruction);
+ boolean unboxing = instruction.getMethodType() == MethodCallInstruction.MethodType.UNBOXING;
+ NullabilityProblem problem = unboxing ? NullabilityProblem.unboxingNullable : NullabilityProblem.callNPE;
+ PsiExpression anchor = unboxing ? instruction.getContext() : instruction.getCallExpression();
+ if (!checkNotNullable(memState, qualifier, problem, anchor)) {
if (qualifier instanceof DfaVariableValue) {
memState.setVarValue((DfaVariableValue)qualifier, runner.getFactory().createTypeValue(
((DfaVariableValue)qualifier).getVariableType(), Nullness.NOT_NULL));
@@ -296,11 +287,11 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return TypeConversionUtil.computeCastTo(o, PsiType.LONG);
}
-
- protected void onInstructionProducesNPE(Instruction instruction) {}
-
- protected void onPassingNullParameter(PsiExpression arg) {}
- protected void onPassingNullParameterToNonAnnotated(DataFlowRunner runner, PsiExpression arg) {}
+ protected boolean checkNotNullable(DfaMemoryState state,
+ DfaValue value, NullabilityProblem problem,
+ PsiElement anchor) {
+ return state.checkNotNullable(value);
+ }
@Override
public DfaInstructionState[] visitBinop(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
@@ -351,11 +342,10 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return null;
}
- if (isViaMethods(dfaLeft) || isViaMethods(dfaRight)) {
- skipConstantConditionReporting(instruction.getPsiAnchor());
- }
myCanBeNullInInstanceof.add(instruction);
+ boolean specialContractTreatment = isUnknownComparisonWithNullInContract(instruction, dfaLeft, dfaRight, factory, memState);
+
ArrayList<DfaInstructionState> states = new ArrayList<DfaInstructionState>();
final DfaMemoryState trueCopy = memState.createCopy();
@@ -363,6 +353,9 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (!dfaRelation.isNegated()) {
checkOneOperandNotNull(dfaRight, dfaLeft, factory, trueCopy);
}
+ if (specialContractTreatment && !dfaRelation.isNegated()) {
+ trueCopy.markEphemeral();
+ }
trueCopy.push(factory.getConstFactory().getTrue());
instruction.setTrueReachable();
states.add(new DfaInstructionState(next, trueCopy));
@@ -374,6 +367,9 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (dfaRelation.isNegated()) {
checkOneOperandNotNull(dfaRight, dfaLeft, factory, falseCopy);
}
+ if (specialContractTreatment && dfaRelation.isNegated()) {
+ falseCopy.markEphemeral();
+ }
falseCopy.push(factory.getConstFactory().getFalse());
instruction.setFalseReachable();
states.add(new DfaInstructionState(next, falseCopy));
@@ -385,12 +381,25 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return states.toArray(new DfaInstructionState[states.size()]);
}
- public void skipConstantConditionReporting(@Nullable PsiElement anchor) {
- ContainerUtil.addIfNotNull(myNotToReportReachability, anchor);
+ private static boolean isUnknownComparisonWithNullInContract(BinopInstruction instruction,
+ DfaValue dfaLeft,
+ DfaValue dfaRight,
+ DfaValueFactory factory,
+ DfaMemoryState memoryState) {
+ if (instruction.getPsiAnchor() != null || dfaRight != factory.getConstFactory().getNull()) {
+ return false;
+ }
+ if (dfaLeft instanceof DfaVariableValue) {
+ return ((DfaMemoryStateImpl)memoryState).getVariableState((DfaVariableValue)dfaLeft).getNullability() == Nullness.UNKNOWN;
+ }
+ if (dfaLeft instanceof DfaTypeValue) {
+ return ((DfaTypeValue)dfaLeft).getNullness() == Nullness.UNKNOWN;
+ }
+ return false;
}
- private static boolean isViaMethods(DfaValue dfa) {
- return dfa instanceof DfaVariableValue && ((DfaVariableValue)dfa).isViaMethods();
+ public void skipConstantConditionReporting(@Nullable PsiElement anchor) {
+ ContainerUtil.addIfNotNull(myNotToReportReachability, anchor);
}
private void handleInstanceof(InstanceofInstruction instruction, DfaValue dfaRight, DfaValue dfaLeft) {
@@ -399,7 +408,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
myCanBeNullInInstanceof.add(instruction);
}
- if (((DfaTypeValue)dfaRight).getType().isAssignableFrom(((DfaTypeValue)dfaLeft).getType())) {
+ if (((DfaTypeValue)dfaRight).getDfaType().isAssignableFrom(((DfaTypeValue)dfaLeft).getDfaType())) {
return;
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java
new file mode 100644
index 000000000000..e886dde637a0
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInspection.dataFlow;
+
+import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
+import com.intellij.codeInspection.dataFlow.value.DfaValue;
+import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.UnorderedPair;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author peter
+ */
+class StateMerger {
+ private final List<DfaMemoryStateImpl> myStates;
+ private final MultiMap<UnorderedPair<DfaValue>,DfaMemoryStateImpl> myStatesByEq = new MultiMap<UnorderedPair<DfaValue>, DfaMemoryStateImpl>();
+ private final Map<DfaMemoryStateImpl, Map<DfaVariableValue, DfaConstValue>> myVarValues = ContainerUtil.newIdentityHashMap();
+
+ public StateMerger(List<DfaMemoryStateImpl> states) {
+ myStates = states;
+ for (DfaMemoryStateImpl state : myStates) {
+ ProgressManager.checkCanceled();
+
+ Map<DfaVariableValue,DfaConstValue> varValues = ContainerUtil.newHashMap();
+ for (UnorderedPair<DfaValue> pair : getEqPairs(state)) {
+ myStatesByEq.putValue(pair, state);
+ if (pair.first instanceof DfaVariableValue && pair.second instanceof DfaConstValue) {
+ varValues.put((DfaVariableValue)pair.first, (DfaConstValue)pair.second);
+ }
+ }
+ myVarValues.put(state, varValues);
+ }
+ }
+
+ @Nullable
+ public List<DfaMemoryStateImpl> merge() {
+ for (final DfaMemoryStateImpl state : myStates) {
+ ProgressManager.checkCanceled();
+ MultiMap<DfaVariableValue, DfaValue> distincts = getDistinctsMap(state);
+ for (DfaVariableValue var : distincts.keySet()) {
+ Map<DfaValue, Collection<DfaMemoryStateImpl>> statesByValue = getCompatibleStatesByValue(state, var, distincts);
+ if (statesByValue == null) {
+ continue;
+ }
+
+ DfaMemoryStateImpl copy = copyWithoutVar(state, var);
+
+ final Set<DfaMemoryStateImpl> complementaryStates = findComplementaryStates(var, statesByValue, copy);
+ if (complementaryStates == null) {
+ continue;
+ }
+
+ complementaryStates.add(state);
+ copy = copy.createCopy();
+ for (DfaMemoryStateImpl removedState : complementaryStates) {
+ for (DfaVariableValue unknownVar : removedState.getUnknownVariables()) {
+ copy.doFlush(unknownVar, true);
+ }
+ if (removedState.isNull(var)) {
+ copy.setVariableState(var, copy.getVariableState(var).withNullability(Nullness.NULLABLE));
+ }
+ }
+
+ List<DfaMemoryStateImpl> result = ContainerUtil.newArrayList();
+ result.add(copy);
+ result.addAll(ContainerUtil.filter(myStates, new Condition<DfaMemoryStateImpl>() {
+ @Override
+ public boolean value(DfaMemoryStateImpl state) {
+ return !complementaryStates.contains(state);
+ }
+ }));
+ return result;
+ }
+
+ }
+ return null;
+ }
+
+ private Map<Pair<DfaMemoryStateImpl, DfaVariableValue>, DfaMemoryStateImpl> myCopyCache = ContainerUtil.newHashMap();
+ private DfaMemoryStateImpl copyWithoutVar(DfaMemoryStateImpl state, DfaVariableValue var) {
+ Pair<DfaMemoryStateImpl, DfaVariableValue> key = Pair.create(state, var);
+ DfaMemoryStateImpl copy = myCopyCache.get(key);
+ if (copy == null) {
+ copy = state.createCopy();
+ copy.flushVariable(var);
+ myCopyCache.put(key, copy);
+ }
+ return copy;
+ }
+
+ @Nullable
+ private Set<DfaMemoryStateImpl> findComplementaryStates(DfaVariableValue var,
+ Map<DfaValue, Collection<DfaMemoryStateImpl>> statesByValue,
+ DfaMemoryStateImpl mainCopy) {
+ Set<DfaMemoryStateImpl> removedStates = ContainerUtil.newTroveSet(ContainerUtil.<DfaMemoryStateImpl>identityStrategy());
+
+ eachValue:
+ for (DfaValue value : statesByValue.keySet()) {
+ for (DfaMemoryStateImpl originalState : statesByValue.get(value)) {
+ if (mainCopy.equalsByRelations(copyWithoutVar(originalState, var))) {
+ removedStates.add(originalState);
+ continue eachValue;
+ }
+ }
+ return null;
+ }
+ return removedStates;
+ }
+
+ @Nullable
+ private Map<DfaValue, Collection<DfaMemoryStateImpl>> getCompatibleStatesByValue(final DfaMemoryStateImpl state,
+ final DfaVariableValue var,
+ MultiMap<DfaVariableValue, DfaValue> distincts) {
+ Map<DfaValue, Collection<DfaMemoryStateImpl>> statesByValue = ContainerUtil.newHashMap();
+ for (DfaValue value : distincts.get(var)) {
+ List<DfaMemoryStateImpl> compatible = ContainerUtil.filter(myStatesByEq.get(createPair(var, value)), new Condition<DfaMemoryStateImpl>() {
+ @Override
+ public boolean value(DfaMemoryStateImpl state2) {
+ return areCompatible(state, state2, var);
+ }
+ });
+ if (compatible.isEmpty()) {
+ return null;
+ }
+ statesByValue.put(value, compatible);
+ }
+ return statesByValue;
+ }
+
+ private boolean areCompatible(DfaMemoryStateImpl state1, DfaMemoryStateImpl state2, DfaVariableValue differentVar) {
+ if (!state1.equalsSuperficially(state2)) {
+ return false;
+ }
+ Map<DfaVariableValue, DfaConstValue> varValues1 = myVarValues.get(state1);
+ Map<DfaVariableValue, DfaConstValue> varValues2 = myVarValues.get(state2);
+
+ for (DfaVariableValue var : varValues1.keySet()) {
+ if (var != differentVar && varValues1.get(var) != varValues2.get(var)) {
+ return false;
+ }
+ }
+ for (DfaVariableValue var : varValues2.keySet()) {
+ if (var != differentVar && !varValues1.containsKey(var)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static MultiMap<DfaVariableValue, DfaValue> getDistinctsMap(DfaMemoryStateImpl state) {
+ MultiMap<DfaVariableValue, DfaValue> distincts = new MultiMap<DfaVariableValue, DfaValue>();
+ for (UnorderedPair<EqClass> classPair : state.getDistinctClassPairs()) {
+ for (DfaValue value1 : classPair.first.getMemberValues()) {
+ value1 = DfaMemoryStateImpl.unwrap(value1);
+ for (DfaValue value2 : classPair.second.getMemberValues()) {
+ value2 = DfaMemoryStateImpl.unwrap(value2);
+ if (value1 instanceof DfaVariableValue) {
+ if (value2 instanceof DfaVariableValue || value2 instanceof DfaConstValue) {
+ distincts.putValue((DfaVariableValue)value1, value2);
+ }
+ }
+ if (value2 instanceof DfaVariableValue) {
+ if (value1 instanceof DfaVariableValue || value1 instanceof DfaConstValue) {
+ distincts.putValue((DfaVariableValue)value2, value1);
+ }
+ }
+ }
+ }
+ }
+ return distincts;
+ }
+
+ private static List<UnorderedPair<DfaValue>> getEqPairs(DfaMemoryStateImpl state) {
+ Set<UnorderedPair<DfaValue>> eqPairs = ContainerUtil.newHashSet();
+ for (EqClass eqClass : state.getNonTrivialEqClasses()) {
+ DfaConstValue constant = eqClass.findConstant(true);
+ List<DfaVariableValue> vars = eqClass.getVariables();
+ for (int i = 0; i < vars.size(); i++) {
+ DfaVariableValue var = vars.get(i);
+ if (constant != null) {
+ eqPairs.add(createPair(var, constant));
+ }
+ for (int j = i + 1; j < vars.size(); j++) {
+ eqPairs.add(createPair(var, vars.get(j)));
+ }
+ }
+ }
+ return ContainerUtil.newArrayList(eqPairs);
+ }
+
+ private static UnorderedPair<DfaValue> createPair(DfaVariableValue var, DfaValue val) {
+ return new UnorderedPair<DfaValue>(var, val);
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java
index e4631d66c79e..87aa89759798 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java
@@ -16,10 +16,14 @@
package com.intellij.codeInspection.dataFlow;
+import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.psi.PsiExpression;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Set;
/**
* @author Gregory.Shrago
@@ -36,9 +40,13 @@ public class ValuableDataFlowRunner extends DataFlowRunner {
super(factory);
}
+ MyDfaMemoryState(DfaMemoryStateImpl toCopy) {
+ super(toCopy);
+ }
+
@Override
- protected DfaMemoryStateImpl createNew() {
- return new MyDfaMemoryState(getFactory());
+ public DfaMemoryStateImpl createCopy() {
+ return new MyDfaMemoryState(this);
}
@Override
@@ -49,21 +57,37 @@ public class ValuableDataFlowRunner extends DataFlowRunner {
}
static class ValuableDfaVariableState extends DfaVariableState {
- DfaValue myValue;
- PsiExpression myExpression;
+ final DfaValue myValue;
+ final PsiExpression myExpression;
private ValuableDfaVariableState(final DfaVariableValue psiVariable) {
super(psiVariable);
+ myValue = null;
+ myExpression = null;
+ }
+
+ private ValuableDfaVariableState(Set<DfaPsiType> instanceofValues,
+ Set<DfaPsiType> notInstanceofValues,
+ Nullness nullability, DfaValue value, PsiExpression expression) {
+ super(instanceofValues, notInstanceofValues, nullability);
+ myValue = value;
+ myExpression = expression;
}
- protected ValuableDfaVariableState(final ValuableDfaVariableState state) {
- super(state);
- myExpression = state.myExpression;
+ @Override
+ protected DfaVariableState createCopy(Set<DfaPsiType> instanceofValues, Set<DfaPsiType> notInstanceofValues, Nullness nullability) {
+ return new ValuableDfaVariableState(instanceofValues, notInstanceofValues, nullability, myValue, myExpression);
}
@Override
- public void setValue(final DfaValue value) {
- myValue = value;
+ public DfaVariableState withValue(@Nullable final DfaValue value) {
+ if (value == myValue) return this;
+ return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, myNullability, value, myExpression);
+ }
+
+ public ValuableDfaVariableState withExpression(@Nullable final PsiExpression expression) {
+ if (expression == myExpression) return this;
+ return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, myNullability, myValue, expression);
}
@Override
@@ -72,8 +96,25 @@ public class ValuableDataFlowRunner extends DataFlowRunner {
}
@Override
- protected ValuableDfaVariableState clone() {
- return new ValuableDfaVariableState(this);
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ValuableDfaVariableState)) return false;
+ if (!super.equals(o)) return false;
+
+ ValuableDfaVariableState state = (ValuableDfaVariableState)o;
+
+ if (myExpression != null ? !myExpression.equals(state.myExpression) : state.myExpression != null) return false;
+ if (myValue != null ? !myValue.equals(state.myValue) : state.myValue != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (myValue != null ? myValue.hashCode() : 0);
+ result = 31 * result + (myExpression != null ? myExpression.hashCode() : 0);
+ return result;
}
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java
index dd2514c41f8e..6e64a6b86416 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java
@@ -33,6 +33,7 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.JavaTokenType.*;
@@ -41,11 +42,10 @@ public class BinopInstruction extends BranchingInstruction {
private final IElementType myOperationSign;
private final Project myProject;
- public BinopInstruction(IElementType opSign, PsiElement psiAnchor, @NotNull Project project) {
+ public BinopInstruction(IElementType opSign, @Nullable PsiElement psiAnchor, @NotNull Project project) {
+ super(psiAnchor);
myProject = project;
myOperationSign = ourSignificantOperations.contains(opSign) ? opSign : null;
-
- setPsiAnchor(psiAnchor);
}
@Override
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BranchingInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BranchingInstruction.java
index d1524fc42092..0f83d84afd28 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BranchingInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BranchingInstruction.java
@@ -27,17 +27,19 @@ package com.intellij.codeInspection.dataFlow.instructions;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLiteralExpression;
import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
public abstract class BranchingInstruction extends Instruction {
private boolean myIsTrueReachable;
private boolean myIsFalseReachable;
- private boolean isConstTrue;
- private PsiElement myExpression;
+ private final boolean isConstTrue;
+ private final PsiElement myExpression;
- protected BranchingInstruction() {
+ protected BranchingInstruction(@Nullable PsiElement psiAnchor) {
myIsTrueReachable = false;
myIsFalseReachable = false;
- setPsiAnchor(null);
+ myExpression = psiAnchor;
+ isConstTrue = psiAnchor != null && isBoolConst(psiAnchor);
}
public boolean isTrueReachable() {
@@ -70,8 +72,4 @@ public abstract class BranchingInstruction extends Instruction {
return "true".equals(text) || "false".equals(text);
}
- protected void setPsiAnchor(PsiElement psiAnchor) {
- myExpression = psiAnchor;
- isConstTrue = psiAnchor != null && isBoolConst(psiAnchor);
- }
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ConditionalGotoInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ConditionalGotoInstruction.java
index 2b93e95045cb..212a02f361ad 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ConditionalGotoInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ConditionalGotoInstruction.java
@@ -33,9 +33,9 @@ public class ConditionalGotoInstruction extends BranchingInstruction {
private final boolean myIsNegated;
public ConditionalGotoInstruction(ControlFlow.ControlFlowOffset myOffset, boolean isNegated, PsiElement psiAnchor) {
+ super(psiAnchor);
this.myOffset = myOffset;
myIsNegated = isNegated;
- setPsiAnchor(psiAnchor);
}
public boolean isNegated() {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GosubInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GosubInstruction.java
index 9d2a5944c138..dc1fd6fc6f74 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GosubInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GosubInstruction.java
@@ -27,15 +27,19 @@ public class GosubInstruction extends Instruction {
mySubprogramOffset = subprogramOffset;
}
+ public int getSubprogramOffset() {
+ return mySubprogramOffset.getInstructionOffset();
+ }
+
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
final int returnIndex = getIndex() + 1;
stateBefore.pushOffset(returnIndex);
- Instruction nextInstruction = runner.getInstruction(mySubprogramOffset.getInstructionOffset());
+ Instruction nextInstruction = runner.getInstruction(getSubprogramOffset());
return new DfaInstructionState[] {new DfaInstructionState(nextInstruction, stateBefore)};
}
public String toString() {
- return "GOSUB: " + mySubprogramOffset.getInstructionOffset();
+ return "GOSUB: " + getSubprogramOffset();
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GotoInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GotoInstruction.java
index 328609095456..c4400f771fcc 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GotoInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/GotoInstruction.java
@@ -34,14 +34,18 @@ public class GotoInstruction extends Instruction {
this.myOffset = myOffset;
}
+ public int getOffset() {
+ return myOffset.getInstructionOffset();
+ }
+
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
- Instruction nextInstruction = runner.getInstruction(myOffset.getInstructionOffset());
+ Instruction nextInstruction = runner.getInstruction(getOffset());
return new DfaInstructionState[]{new DfaInstructionState(nextInstruction, stateBefore)};
}
public String toString() {
- return "GOTO: " + myOffset.getInstructionOffset();
+ return "GOTO: " + getOffset();
}
public void setOffset(final int offset) {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/Instruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/Instruction.java
index 5514268233cd..096188ec1e8e 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/Instruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/Instruction.java
@@ -28,18 +28,9 @@ import com.intellij.codeInspection.dataFlow.DataFlowRunner;
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
-import com.intellij.openapi.progress.ProgressManager;
-
-import java.util.ArrayList;
-import java.util.List;
public abstract class Instruction {
private int myIndex;
- private final List<DfaMemoryState> myProcessedStates;
-
- protected Instruction() {
- myProcessedStates = new ArrayList<DfaMemoryState>();
- }
protected final DfaInstructionState[] nextInstruction(DataFlowRunner runner, DfaMemoryState stateBefore) {
return new DfaInstructionState[] {new DfaInstructionState(runner.getInstruction(getIndex() + 1), stateBefore)};
@@ -47,23 +38,6 @@ public abstract class Instruction {
public abstract DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor);
- public boolean isMemoryStateProcessed(DfaMemoryState dfaMemState) {
- for (DfaMemoryState state : myProcessedStates) {
- ProgressManager.checkCanceled();
- if (dfaMemState.equals(state)) {
- return true;
- }
- }
-
- return false;
- }
-
- public boolean setMemoryStateProcessed(DfaMemoryState dfaMemState) {
- if (myProcessedStates.size() > DataFlowRunner.MAX_STATES_PER_BRANCH) return false;
- myProcessedStates.add(dfaMemState);
- return true;
- }
-
public void setIndex(int index) {
myIndex = index;
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaPsiType.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaPsiType.java
new file mode 100644
index 000000000000..f58e44fc83e4
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaPsiType.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInspection.dataFlow.value;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiType;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Map;
+
+/**
+ * @author peter
+ */
+public class DfaPsiType {
+ private final PsiType myPsiType;
+ private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myAssignableCache;
+ private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myConvertibleCache;
+
+ DfaPsiType(@NotNull PsiType psiType, Map<Pair<DfaPsiType, DfaPsiType>, Boolean> assignableCache, Map<Pair<DfaPsiType, DfaPsiType>, Boolean> convertibleCache) {
+ myPsiType = psiType;
+ myAssignableCache = assignableCache;
+ myConvertibleCache = convertibleCache;
+ }
+
+ @NotNull
+ public PsiType getPsiType() {
+ return myPsiType;
+ }
+
+ public boolean isAssignableFrom(DfaPsiType other) {
+ if (other == this) return true;
+ Pair<DfaPsiType, DfaPsiType> key = Pair.create(this, other);
+ Boolean result = myAssignableCache.get(key);
+ if (result == null) {
+ myAssignableCache.put(key, result = myPsiType.isAssignableFrom(other.myPsiType));
+ }
+ return result;
+ }
+
+ public boolean isConvertibleFrom(DfaPsiType other) {
+ if (other == this) return true;
+ Pair<DfaPsiType, DfaPsiType> key = Pair.create(this, other);
+ Boolean result = myConvertibleCache.get(key);
+ if (result == null) {
+ myConvertibleCache.put(key, result = myPsiType.isConvertibleFrom(other.myPsiType));
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return myPsiType.getPresentableText();
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaTypeValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaTypeValue.java
index f7b648762365..193d3426ea29 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaTypeValue.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaTypeValue.java
@@ -25,72 +25,51 @@
package com.intellij.codeInspection.dataFlow.value;
import com.intellij.codeInspection.dataFlow.Nullness;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.psi.PsiKeyword;
-import com.intellij.psi.PsiType;
-import com.intellij.psi.util.TypeConversionUtil;
-import com.intellij.util.containers.HashMap;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
+import java.util.Map;
public class DfaTypeValue extends DfaValue {
public static class Factory {
- private final DfaTypeValue mySharedInstance;
- private final HashMap<String,ArrayList<DfaTypeValue>> myStringToObject;
+ private final Map<DfaPsiType,ArrayList<DfaTypeValue>> myCache = ContainerUtil.newHashMap();
private final DfaValueFactory myFactory;
Factory(DfaValueFactory factory) {
myFactory = factory;
- mySharedInstance = new DfaTypeValue(factory);
- myStringToObject = new HashMap<String, ArrayList<DfaTypeValue>>();
}
@NotNull
- public DfaTypeValue createTypeValue(@NotNull PsiType type, @NotNull Nullness nullable) {
- type = TypeConversionUtil.erasure(type);
- mySharedInstance.myType = type;
- mySharedInstance.myCanonicalText = StringUtil.notNullize(type.getCanonicalText(), PsiKeyword.NULL);
- mySharedInstance.myNullness = nullable;
-
- String id = mySharedInstance.toString();
- ArrayList<DfaTypeValue> conditions = myStringToObject.get(id);
+ public DfaTypeValue createTypeValue(@NotNull DfaPsiType type, @NotNull Nullness nullness) {
+ ArrayList<DfaTypeValue> conditions = myCache.get(type);
if (conditions == null) {
conditions = new ArrayList<DfaTypeValue>();
- myStringToObject.put(id, conditions);
+ myCache.put(type, conditions);
} else {
for (DfaTypeValue aType : conditions) {
- if (aType.hardEquals(mySharedInstance)) return aType;
+ if (aType.myNullness == nullness) return aType;
}
}
- DfaTypeValue result = new DfaTypeValue(type, nullable, myFactory, mySharedInstance.myCanonicalText);
+ DfaTypeValue result = new DfaTypeValue(type, nullness, myFactory);
conditions.add(result);
- return result;
+ return new DfaTypeValue(type, nullness, myFactory);
}
- public DfaTypeValue createTypeValue(@NotNull PsiType type) {
- return createTypeValue(type, Nullness.UNKNOWN);
- }
}
- private PsiType myType;
- private String myCanonicalText;
+ private DfaPsiType myType;
private Nullness myNullness;
- private DfaTypeValue(DfaValueFactory factory) {
- super(factory);
- }
-
- private DfaTypeValue(PsiType type, Nullness nullness, DfaValueFactory factory, String canonicalText) {
+ private DfaTypeValue(DfaPsiType type, Nullness nullness, DfaValueFactory factory) {
super(factory);
myType = type;
myNullness = nullness;
- myCanonicalText = canonicalText;
}
- public PsiType getType() {
+ public DfaPsiType getDfaType() {
return myType;
}
@@ -102,23 +81,13 @@ public class DfaTypeValue extends DfaValue {
return myNullness == Nullness.NOT_NULL;
}
- @NonNls
- public String toString() {
- return myCanonicalText + ", nullable=" + myNullness;
+ public Nullness getNullness() {
+ return myNullness;
}
- private boolean hardEquals(DfaTypeValue aType) {
- return myCanonicalText.equals(aType.myCanonicalText) && myNullness == aType.myNullness && myType.equals(aType.myType);
- }
-
- public boolean isAssignableFrom(DfaTypeValue dfaType) {
- return dfaType != null && myType.isAssignableFrom(dfaType.myType);
+ @NonNls
+ public String toString() {
+ return myType + ", nullable=" + myNullness;
}
- public boolean isConvertibleFrom(DfaTypeValue dfaType) {
- if (dfaType == null) return false;
- assert myType.isValid() : "my type invalid";
- assert dfaType.myType.isValid() : " their type invalid";
- return myType.isConvertibleFrom(dfaType.myType);
- }
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValue.java
index e19b3ce2125e..c78bfb149d6a 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValue.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValue.java
@@ -21,13 +21,7 @@ public class DfaValue {
protected DfaValue(final DfaValueFactory factory) {
myFactory = factory;
- if (factory == null) {
- myID = 0;
- }
- else {
- myID = factory.createID();
- factory.registerValue(this);
- }
+ myID = factory == null ? 0 : factory.registerValue(this);
}
public int getID() {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java
index f6b900f9f85d..70cb578bd4fa 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java
@@ -25,24 +25,26 @@
package com.intellij.codeInspection.dataFlow.value;
import com.intellij.codeInspection.dataFlow.Nullness;
-import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import com.intellij.psi.util.PsiTreeUtil;
-import gnu.trove.TIntObjectHashMap;
+import com.intellij.psi.util.TypeConversionUtil;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-public class DfaValueFactory {
- private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.value.DfaValueFactory");
+import java.util.List;
+import java.util.Map;
- private int myLastID;
- private final TIntObjectHashMap<DfaValue> myValues;
+public class DfaValueFactory {
+ private final List<DfaValue> myValues = ContainerUtil.newArrayList();
+ private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myAssignableCache = ContainerUtil.newHashMap();
+ private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myConvertibleCache = ContainerUtil.newHashMap();
+ private final Map<PsiType, DfaPsiType> myDfaTypes = ContainerUtil.newHashMap();
public DfaValueFactory() {
- myValues = new TIntObjectHashMap<DfaValue>();
- myLastID = 0;
-
+ myValues.add(null);
myVarFactory = new DfaVariableValue.Factory(this);
myConstFactory = new DfaConstValue.Factory(this);
myBoxedFactory = new DfaBoxedValue.Factory(this);
@@ -52,17 +54,20 @@ public class DfaValueFactory {
public DfaValue createTypeValue(@Nullable PsiType type, Nullness nullability) {
if (type == null) return DfaUnknownValue.getInstance();
- return getTypeFactory().createTypeValue(type, nullability);
+ return getTypeFactory().createTypeValue(internType(type), nullability);
}
- int createID() {
- myLastID++;
- LOG.assertTrue(myLastID >= 0, "Overflow");
- return myLastID;
+ private DfaPsiType internType(@NotNull PsiType psiType) {
+ DfaPsiType dfaType = myDfaTypes.get(psiType);
+ if (dfaType == null) {
+ myDfaTypes.put(psiType, dfaType = new DfaPsiType(TypeConversionUtil.erasure(psiType), myAssignableCache, myConvertibleCache));
+ }
+ return dfaType;
}
- void registerValue(DfaValue value) {
- myValues.put(value.getID(), value);
+ int registerValue(DfaValue value) {
+ myValues.add(value);
+ return myValues.size() - 1;
}
public DfaValue getValue(int id) {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java
index e522b8fb2c6f..fa2217a2918b 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java
@@ -30,6 +30,7 @@ import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Trinity;
import com.intellij.psi.*;
+import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
@@ -90,7 +91,7 @@ public class DfaVariableValue extends DfaValue {
myIsNegated = isNegated;
myQualifier = qualifier;
myVarType = varType;
- myTypeValue = varType == null ? null : myFactory.getTypeFactory().createTypeValue(varType, Nullness.UNKNOWN);
+ myTypeValue = varType == null ? null : (DfaTypeValue)myFactory.createTypeValue(varType, Nullness.UNKNOWN);
}
@Nullable
@@ -132,7 +133,7 @@ public class DfaVariableValue extends DfaValue {
private boolean hardEquals(PsiModifierListOwner psiVar, PsiType varType, boolean negated, DfaVariableValue qualifier) {
return psiVar == myVariable &&
- Comparing.equal(varType, myVarType) &&
+ Comparing.equal(TypeConversionUtil.erasure(varType), TypeConversionUtil.erasure(myVarType)) &&
negated == myIsNegated &&
(myQualifier == null ? qualifier == null : myQualifier.hardEquals(qualifier.getPsiVariable(), qualifier.getVariableType(),
qualifier.isNegated(), qualifier.getQualifier()));
@@ -143,10 +144,6 @@ public class DfaVariableValue extends DfaValue {
return myQualifier;
}
- public boolean isViaMethods() {
- return myVariable instanceof PsiMethod || myQualifier != null && myQualifier.isViaMethods();
- }
-
public Nullness getInherentNullability() {
if (myInherentNullability != null) {
return myInherentNullability;
@@ -190,14 +187,12 @@ public class DfaVariableValue extends DfaValue {
return Nullness.UNKNOWN;
}
- public boolean isLocalVariable() {
- return myVariable instanceof PsiLocalVariable || myVariable instanceof PsiParameter;
- }
-
public boolean isFlushableByCalls() {
- if (isLocalVariable()) return false;
- if (!myVariable.hasModifierProperty(PsiModifier.FINAL)) return true;
- return myQualifier != null && myQualifier.isFlushableByCalls();
+ if (myVariable instanceof PsiLocalVariable || myVariable instanceof PsiParameter) return false;
+ if (myVariable instanceof PsiVariable && myVariable.hasModifierProperty(PsiModifier.FINAL)) {
+ return myQualifier != null && myQualifier.isFlushableByCalls();
+ }
+ return true;
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java
index 71fa80a2077f..c46bfb656769 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java
@@ -223,7 +223,7 @@ public class RefJavaUtilImpl extends RefJavaUtil{
refParent = refParent.getOwner();
}
- return (RefClass)refElement;
+ return refElement instanceof RefClass ? (RefClass)refElement : null;
}
@Override