summaryrefslogtreecommitdiff
path: root/java/java-psi-impl/src/com/intellij/psi
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2013-09-19 12:39:22 -0700
committerTor Norbye <tnorbye@google.com>2013-09-19 12:40:05 -0700
commitbeca9839b2866f90da9dc517c29df2ec25a6f6a8 (patch)
tree747d1f98670ee652ac6637ad488124760b2ac489 /java/java-psi-impl/src/com/intellij/psi
parenta28de544d7f6bae0c637a1f8175ea613a976c52e (diff)
downloadidea-beca9839b2866f90da9dc517c29df2ec25a6f6a8.tar.gz
Snapshot 09776de1457414bbc7dc8a09e242de80e282d7dc from idea/132.325 of git://git.jetbrains.org/idea/community.git
09776de: IDEA-97132 "Building Gradle project info" dialog is modal and non-cancelable 8013ffd: Merge remote-tracking branch 'origin/master' 870d88c: IDEA-113775 External System: new 'Refresh external project' action for single module refresh; shortcut changed to "use-shortcut-of='Refresh'" 8c31855: JediTerm jars updated. 6e01d2c: remove unused dependency of dvcs on groovy 206b35f: allow to filter out inspection highlight infos (IDEA-113738) 0b123a7: pass PsiElement to be able to filter out specific highlight infos based on location (IDEA-113738) 85ca653: do we have java.applet in mockjdk now? 7c0ff90: more diagnostics in buildModulesAndCollectUsedJars() a99e7af: NSIS build scripts moved to community 3f2c72f: robust removeAllBreakpoints catch error while open new tab (attach debugger) Chrome still crashed on speed search in Variable View. bedb5d8: move NsiFiles helper task to community 26e1d16: new inference: do not imply constraints for proper types e273976: new inference: null upper bounds 278120e: move DefaultColorScheme* stuff to editor-ui-ex module e0ed326: Add reference for dependenciesToScan parameter of maven-surefire-plugin 79f4809: IDEA-112202 Ability to open surefire-reports in tests view like Eclipse does Add reference to open test result folder a0d65ce: IDEA-113766 Incorrect font width calculation in text area 44e095e: fix Terminal issues 34b1575: new project wizard: fixing layout problems 077e89f: IDEA-98656 Java doc not generated when an other Java doc has been commented after. 1554b75: IDEA-113604 Error suppression does not stop freemarker validator 5c0d022: IDEA-113596 Can't copy "About" details while indexing a645075: use optimized changes calculation always and only report different values in case of EAP mode c37d75d: IDEA-113581 Gradle: New Project Wizard: it could be possible to enable "Use default gradle wrapper" option 3b277d2: new project wizard: fixing layout problems 6e39762: new project wizard: hiding the "select desired technologies" label 5c1c1b0: new inference: infer from condition 052290e: NPE c1137ac: new inference: promote extends bounds to lower level 6950859: new inference: accept bounds with recursive dependency 92f6ae2: new inference: open top level captured wildcards 1622968: new inference: use dependant variables instantiations when resolve bounds e980a77: IDEA-113581 Gradle: New Project Wizard: it could be possible to enable "Use default gradle wrapper" option b3d49a4: IDEA-113775 External System: new 'Refresh external project' action for single module refresh c0afc26: fix stringWidth on non Retina devices e5781d7: IDEA-110452 Darcula theme causes font size increase 92b5fac: Don't add actions without icons to the file history toolbar 95df681: new project wizard: AddSupportForFrameworksPanel reused bcd0639: EA-49794 - NPE fix fcde853: Gradle: vmOptions provisioning fix 83ef088: IDEA-113199 Grails Plugins manager uses application.properties instead of BuildConfig.groovy Disable Grails plugin dialog for Grails 2.3.0 cae8718: platform: detect case-only name changes e19b820: Cleanup (minor refactoring and optimization) 620b9a1: common super-class extracted for library-based frameworks 60b4a4c: unnecessary intermediate class removed fdec7af: deprecate RoamingTypeExtensionPoint 09faeb9: deprecate RoamingTypeDisabled 2bdc447: [Serega Vasiliev] don't share statistics.application.usages.xml d8c1328: new StreamProvider API: client must not worry about supplementary files (version files for example) cd5f5bf: we don't need to manage list of stream providers, one is enough 3e0ccdd: unused import removed a49963f: Gradle: vmOptions provisioning fix e39cc01: IDEA-113448 Ability to create tests for inner classes 8f64aec: IDEA-113581 Gradle: New Project Wizard: it could be possible to enable "Use default gradle wrapper" option 2ff3757: xdebugger: implemented standalone variables view 93a2c9e: xdebugger: debugger trees decoupled from XDebugSession 7469f4b: notnullification 58c7e6e: hide flex modules from 'available elements' tree in artifact editor c6e955e: ability to cancel write to local file 6999b95: icon classes regenerated: copyright added to generated files, more instructive comment added 4583d6d: action component should be transparent b966b4e: IDEA-96724 Unable to clear Recent Projects list anymore 36c939f: do not handle SelectAll 9007f1e: Merge remote-tracking branch 'origin/master' 0c020d5: JediTerm jars updated. bdd7827: IDEA-98275 (Inspection "Unused assignment" does not work with the += operator) 2366f48: reduce flashes in lens mode da1f984: Lense mode patches&fixes (still internal mode only) 2926b90: unescape unicode escape sequences with a selection 33917b2: Merge remote-tracking branch 'origin/master' db74d04: cleanup aa8b725: keep apple.awt dependencies to util module f5b050d: dfa: boxed/unboxed values are also unknown 68499e9: re: tina 7db91db: For 'normal indices' schedule the file for update and reset stamps for all affected indices (there can be client that used indices between before and after events, in such case indices are up to date due to force update with old content) cee92af: WEB-1212 Add actions "expand children" and "expand descendants" in object variable context menu in JS debug Actually, we don't add such actions, but now speed search can expand nodes. 491868c: Tests updated for system dependent problems. b32093e: Fix 'compare with' on windows for file history. b4e6e2e: IDEA-113622 IDEA-101040 Throw assertion error + add a javadoc 02f8059: groovy for groovy shell 34464a1: cleanup a5c6842: Platform: more precise string version comparison +review CR-OC-818 c5daac1: Merge remote-tracking branch 'origin/master' c9e64f9: Merge branch 'svn1_8_new' 88df6b0: fix dfa false positive in JSTypeofTypeImpl 0a8c128: DfaMemoryStateImpl: use only non-trivial equality classes for comparison fb5e43a: IDEA-94942 Do not add '@' to the path end if no peg revision specified and path does not contain '@' 6eec3b3: IDEA-113725 Remove adding '@' to the checkout destination folder path end 88a8cc6: Enable action for generic fileEditors (non-text) & cleanup 9f1e568: fix NPE on startup with empty configs 57ec7ae: IDEA-108294 every time I open project — it is collapsed to (0.0) fda2edf: IDEA-103303 "BeforeLaunch" tools in run/debug configurations do not do macro substitution (After review http://crucible.labs.intellij.net/cru/CR-IC-2352) 1b52604: new titled renderer 01db28a: rollback 6e2e668: fix stringWidth on Windows 016affd: setup AA as well as fractional metrics b60b5ae: + custom foreground as a parameter e07e365: remove useless warning "Client-side debugging is available in Chrome and Firefox only" 5a773cb: IDEA-113719 Ide frame collapses if a project has been saved with maximised frame on Mac OS X and jdk7 2c7c801: WEB-9277 Exception when startup page in config doesn't start with http:// bad42ce: resource roots: added button to mark directory as a resource root to the toolbar of 'Sources' tab in module editor 82869db: simplification 92ba709: StartBrowserPanel UI fixed after kb review — "Start Browser" checkbox renamed to "After launch", don't disable all panel controls on unselect this checkbox remove label for URL field fix combobox text baseline (only Mac&Acua LAF, other LAFs are not checked) c495a55: cleanup e810a1d: new StreamProvider API: saveContent accept byte[] instead of InputStream 48c3963: hash as primitive int, saveForProviders - avoid document copy e4e62de: IDEA-113678 NPE in external Make with Eclipse compiler 1e0f096: IDEA-95943 Mac OS X, application menu disappears for project sometimes daad75e: Minor code change: optimization 9872570: EA-37570 - IAE: MavenRootModelAdapter.addModuleDependency c110f6a: added icons for resource roots 87a043e: temporary exclude darcula icons from generation 092a4c0: icons classes generation: 'external-system' module migrated to use generated class 2c4952d: Merge remote-tracking branch 'origin/master' dc39410: IDEA-71176 Encapsulate Java fields should update Groovy usages 5de8769: EA-48031 - assert: PsiJavaCodeReferenceElementImpl.diagnoseUnknownParent d2f8238: EA-47448 - SOE: remove GPP traits which cause SOE 3e1a9aa: encode only known references d15350e: batch highlighter dispose requests 2c2111f: avoid deprecated methods ed104a4: refresh todo configuration on color scheme change 02bfaee: cleanup ee5cc85: NPE 17ec6e7: cleanup 2c44b32: visibility 7a12666: AllIcons class regenerated: copyright removed b724720: delete outputs corresponding to changed files for each target before running builders on it f2cb5f3: new inference: initial lambda 0959d41: new inference: delayed constraints 74364db: new inference: accept all new expressions when diamond is checked to support collapse checks 7a06e5d: platform: less Win32 FS cache misses 2fe6eea: EA-47867 (keep order of path macros) 24caf16: Cleanup (unneeded qualifier) cae47e4: model: meaningful macro name assertion cdc9cc1: Cleanup (typos) cf7580c: tests framework: project descriptor creation eased 2d83d16: tests: nothing to present for the public 24f5418: IDEA-113686 Unable to debug gradle tests when -Dtest.single is used c42bb71: correctly handle case changes in resource files for case-insensitive file systems: ensure case of the output file is exactly the same as the case of the source file 6375979: new inference: typo b6d38c0: new inference with diamonds: find in map by context arguments 46e8363: diamonds: unify substitutor calculation 4c9117d: class moved to proper source root d831db7: icons classes generation: fixed regex processing in icon-robots.txt files, android finally excluded from generation 0a73b24: icon classes regenerated; please don't modify icons classes by hand, use icons.gant instead 2920a3e: EA-50043 - UOE: BaseGenerateTestSupportMethodAction$MyHandler.invoke addc304: inspection profile: allow to change inspection settings from quick fix - profileChanged would force diff to default recalculation (IDEA-113650) 39e0079: NPE c20b88c: new inference: fix subtyping direction 005a409: new inference: incomplete exprs 7b431f4: new inference: wildcard bounds could mention type vars aa8f107: StorageUtil.documentToBytes - we should use standard line-separator (\n) - stream provider can share file content on any OS Only stream provider — in case of local file we use system line separator as before 592744f: cleanup 63d4c9b: component version as TObjectLongHashMap fe5954c: cleanup a109761: IDEA-103303 "BeforeLaunch" tools in run/debug configurations do not do macro substitution 6656c26: remove white stripe under Aqua 4cba712: IDEA-113617 (Inspection "Comparator class not declared serializable" proposes wrong modification for extending interface.) 2253395: new StreamProvider API: saveContent accept size as int, listSubFiles returns Collection<String> instead of String[], default impl listSubFiles moved to interface class use new StreamProvider API in ICS plugin fd10033: building searchable options should also be available in community build scripts c142fea: extract a useful function from ultimate build script 7e43f9c: make peter less worried b3ac677: dfa: closure states have empty stack initially c8e66db: flush code block variables when leaving it via exception route d046951: dfa: don't create negated value just to flush it 36a4b9b: DfaVariableValue: not-null psi, store qualified references in the value itself 2401bcc: DfaVariableValue: cheaper creation, cache negated var 67ecf76: a more explicit CommandLog category 3aa48b4: Use JBColor. c33135a: VCS: improve ChangesTreeList selection in Tree mode ec73fec: StartBrowserPanel & JS Debug RC editor — unified url text field 450ff3c: Merge remote-tracking branch 'origin/master' c9064eb: Merge branch 'svn1_8_new' d89ff92: new inference: find method by arg list fixed dcdaebd: new inference: process all vararg args to gather constraints 903f47b: new inference: stop when bound set is fully inferred 2198276: new inference: subtyping with anonymous fixes b36107d: new inference: exclude return type constraint from applicability check e2ee888: new inference: reuse getTargetType initial e6d1b14: new inference: do not infer non-generics calls 48e206d: new inference: equals & hashCode 314333d: IDEA-94942 Make svn command line implementation always use "interactive authentication" (fixes error when "passive authentication" was applied on some threads where thread local storage was not correctly cleared) f3ba5c7: cleanup, apply on name change fix & NPE fix 1b15d56: assertion fix d12f8f3: bigger font size in Project View d78422c: IDEA-94942 Use move destination as working directory for "svn move" command (not to lock source directory by running process) 3d8cdeb: should be undo/redo in TextFieldWithBrowseButton 7b3500d: move StartBrowserSettings to xml module, use our robust Url instead of java URI 8f9926f: IDEA-102165 android xml: show icons and quickdoc for views in code completion 5eea60f: IDEA-113664 Do not use '@' (for peg revision) at the end of move destination a17d4b6: new "Replace Octal Escape with Unicode Escape" intention ff1983d: IDEA-84569 Gradle - IntelliJ warning about 'main' in sourceSet 8f155fa: EfficientChooseByNameModel -> ChooseByNameModelEx EfficientChooseByNameContributor ->ChooseByNameContributorEx 7401dc6: two inner classes instead of anonymous ones for clarity 8076c8c: Merge remote-tracking branch 'origin/master' d7679bb: XmlElementStorage must pass any files to stream providers, project files too: step 1 — adapter for old StreamProvider interface (such old implementations will not get project files as before, only new new implementations). According to reviewer's suggestion, new interface is not part of platform-api, it is our impl interface 8d65d2e: don't expose getStartupPageField afaaeb4: StartBrowserPanel should not require usage SwingHelper.wrapWithHorizontalStretch, add clearBorder 53f4029: simplify saveForProviders 7c3e74c: Merge branch 'svn1_8_new' 24a4e13: Cleanup ExpressionUtils duplication 0bcf6a3: IDEA-112854 Save credentials to memory cache (for command line implementation) 851c574: IDEA-113555 void in right side of assignment de35189: EA-49916 diagnostics 5083a96: IDEA-113117 Groovy: NPE at GrIntroduceConstantDialog.initializeTargetClassEditor() on attempt to introduce a constant to an anonymous class 91f27f1: process only groovy files 5b636c4: IDEA-94942 Refactored utility cleanup command to use SvnLineCommand (instead of SvnSimpleCommand) 1f2c85e: fix testdata d92bbf1: IDEA-94942 Refactored info client to use SvnLineCommand (instead of SvnSimpleCommand) 780efca: allow to use framework-specific libraries from application servers installations 49312c3: IDEA-112821 IDEA falls in endless loop during editing run configurations f24a45e: Workaround for stringWidth in 7u40 03e8f77: IDEA-94942 Refactored update logic - use common behavior for command line svn commands fa37b7b: Platform: JavaDoc for ExecutionTargets +review CR-IC @yole a0c19e2: Platform: compare versions with different number of parts + Version class constructor's arguments order fixed 8b4064a: fix cat walking over keyboard 0fbc2ec: relax dfa time limit for global inspections c5fbb2e: dfa: fix wrong instanceof detection when using several incarnations of the same generic field with different substitutors 8049090: forget old splitter proportions, etc for the sake of less default values (IDEA-113644) 8c2cee3: unnecessary fqn: check for conflict parameterized references 4f24b07: IDEA-94942 Refactored commit logic - use common behavior for command line svn commands b38de05: IDEA-94942 Resolve repository url and command working directory based on command target (file or url) element (for each command line command) 2015083: java: highlighting for statics in anonymous classes 2c8eb0d: Cleanup (formatting) af9ffb8: fix SIOOBE dcfd5ea: Gradle: cosmetic change 6e3c972: platform: commit/dispose allocated model a7b887e: Gradle: changed settings of gradleHomeCache c6c74fa: Gradle: fix external project path option usage 0a138db: IDEA-94942 Refactored LineCommandListener - removed baseDirectory method 99eba34: restore "Fix StringBuilder append" quickfix a5d17029: Merge remote-tracking branch 'origin/master' 7a56ffa: dfa: cleanup block local variables when exiting it 852aaa1: more concise DfaVariableState.toString 0f1494e: dfa: don't multiply eq classes until really needed c2f28d8: VCS: improve ChangesTreeList selection in Tree mode 7cc9d99: Github: add util function a00623d: IDEA-113634 ("Confusing octal escape sequence" inspection misses some cases) e8d6955: before and after template text must be longer for IntentionDescriptionTest e16694e: IDEA-63698 (Inconvertible type (assign double to Integer) not highlighted as error) 0200bab: DfaMemoryState is unfortunately mutable; use defensive copying a808b08: "Replace Unicode Escape with Unescaped Character" intention (IDEA-60661) 11e73a4: allow to use element to the left if element to the right is not available (at the end of the file) ab2c4af: cleanup 91543ed: dfa: treat every getter-like invocation like a field access, don't look inside and don't parse other classes for that 3b8ee6f: global inspections: preallocate less memory for children 7afff0e: global inspections: store parameter references only once 368e989: revert: honor shouldBuildStubFor() for substituted languages 7436173: honor shouldBuildStubFor() for substituted languages 94c7d9b: separate template name for groovy e508172: EA-49855 - assert: TextRange.<init> select start offset for empty statement 353f4b8: EA-49924 - NPE: JavaMethodsConflictResolver.checkSameSignatures 7fa275b: spellchecker: dictionary extended f389b3f: use java LinkedHashMap (why order is not stable?) 7e13241: restore resolve() method with an old signature for old plugins support, mark it as deprecated 42141f3: IDEA-113538 Insert Managed Dependency: does not find artifact based on artifact id 93af907: Gradle: update bundled gradle version to '1.8-rc-1' 6a9eb29: Gradle: update bundled gradle version to '1.8-rc-1' 6bed505: Merge remote-tracking branch 'origin/master' 995ac2b: @InjectMocks also ignored (IDEA-113579) 4d869d9: IDEA-113532 Gradle: use gradle javadocs for script dsl 'quick documentation' enrichment 5769bd9: Merge remote-tracking branch 'origin/master' 9408a08: fix file case 09fbdf3: prefer java paste provider 88c43fb: new inference: fix dependencies b26f1f0: simplify API — TextBrowseFolderListener JSDebug settings editor — file chooser must use the same logic as RC producer 8429da0: Delete unused file with a lot of bugs. 1b6eba8: Update workaround for '--follow' log argument with largefiles extension. b8f0412: Merge remote-tracking branch 'origin/master' e0c2f16: IDEA-94942 Refactored AuthenticationCallback - accept repository url instead of file 269a0de: WEB-9250 Add "Start browser"/"with JavaScript debugger" options to Node.js run/debug configuration b78367f: Fix OC-8214: [',' on the next line] formatting option +review CR-OC @Anton.Makeev 2911086: minor optimization: do not wait for vfs refresh to complete in case build was cancelled cf9c4ae: remove unused import from test data 4c56a28: more assertions 614a73e: IDEA-94942 Require authentication callback for svn command be not null cb122ba: store language level in CoreLanguageLevelProjectExtension fa81fe4: Merge branch 'fix' of git://github.com/bashor/intellij-community into pull102 c51a79a: Fixed TypeConversionUtil#getAllBoxedTypeSupers (after f4a27e8). ProjectRootModificationTracker#getInstance returns NEVER_CHANGED when ProjectRootModificationTracker service not registered. 8c6247d: avoid empty commits, specify author based on system settings b358947: PropertiesComponentImpl must save settings in predictable stable order cf053bf: cleanup c5c0662: WebBrowsersConfiguration must save list of browsers in predictable stable order 1141959: ignore "ver" as well 63ccfea: cleanup f79f498: cleanup: unused code removed da234c7: cleanup: use less specific interface 33ca91f: unused code removed 3e598ed: IDEA-113519 Groovy Shell fails to open with exceptions 8cc385d: A minimal interface sufficient for XVariablesView usage extracted from XDebugSession. 0ab182e: drop obsolete bkwrd-compatibility 2dc69e5: cleanup e70fcab: not every field with non-annotated constructor initializer is notnull 6af48bb: dfa: fix & treatment 1af2b84: typos fbc3d18: IDEA-111450 false positive for "Redundant type cast" inspection b816d27: mission: new inference (initial) ff775ca: external annos: spare some findFileByUrl aea6b5b: IDEA-94942 Make "svn info <url>" work correctly - do not require File instance in parse logic 9304e4b: IDEA-94942 Make path to svn command line client be obtained only once 6e1d4ee: IDEA-98756 IDEA is unable to resolve AspectJ compiler JAR if Maven 3 importing is on 4262c63: ...create a file :) f6fc373: dfa: use more efficient references to common exception classes 9f6948f: IDEA-94942 Refactored commit logic - removed SvnBindClient ea61355: Do you want to create a file named xxx instead? 4e10a46: ExternalAnnotator: compatibility with older clients is restored; typo fixed in collectInformation name +review CR-IC-2315 cfd6567: IDEA-59783 Properties files: code completion list does not open after dot character e3335f6: ExternalAnnotator documentation is improved +review CR-IC-2315 89087c5: IDEA-113375 "Cyclic expand word" should expand words-separated-by-dashes too d10236b: revert "add possibility to setup lookup element for XML attribute values; IDEA-102167 layout_* attributes should go first" 7b2249b: Add ResolveContext object shared between all scheduled resolve tasks; IDEA-113446 Do not clear data in AndroidExternalApklibDependenciesManager. Instead, store cache in ResolveContext to avoid several resolves of one apklib artifact 7e1bcf2: support conversion of artifact settings d1cfd20: IntelliLang: error filter configuration fix & rename e4d31ad: IDEA-94942 Refactored method CommandUtil.putChangeLists (use concrete type instead of Object) 1c11e83: IDEA-113488 Show commit in git log from file history context menu 8415851: IDEA-94942 Changed log level from "warn" to "info" (in svn command execution logic) 24effbd: Merge branch 'master' of git://github.com/asedunov/intellij-community into pull101 c2ea835: 'prepare plugin for deployment' action creates separate jars for modules included into classpath of external build 88a2716: external annotator is allowed to decide if it should handle file with error +review CR-IC d2de078: IDEA-90511 Slow version comparison made faster. d9224ad: assertion fixed f2bf952: IDEA-113312 Do not fail svn command if could not determine working copy root to execute cleanup (after authentication failure) 315ec89: EA-49525 - assert: TextRange.<init> c052ec4: new project wizard: no checkboxes in header 22a8608: simplify JavaScriptDebugStarter — don't implement — consider to implement com.jetbrains.javascript.debugger.FileUrlMapper instead of providing mappings directly 9c8f60b: Merge remote-tracking branch 'origin/master' c6635f0: IDEA-112456 Show "Cleanup" link on "working copies information panel" for svn 1.8 working copies ace9afb: continue extract "Start Browser" functionality from J2EE to platform xml — JavaEEJavaScriptDebugStarter unified 3639fb1: extract "Start Browser" functionality from J2EE to platform xml, remove outdated BrowserLauncher IDEA-23782 tomcat run configuration: list all known browsers 97f8c64: remove deprecated method escapeUrl 59c7b63: IDEA-112794 Explicitly check that current logic could return "null" as svn command exit code 3af18cf: WEB-8397 Incorrect "element is not allowed here" inspection 840d7aa: cleanup 754959a: moved to psi b15f4f0: IDEA-113432 Groovy script classpath should include module dependencies d2030a5: provide default sensible implementation 4f6aadc: EA-49793 - NPE: FilePointerPartNode.update e87e071: customize GeneralHighlightingPass with - HighlightInfoProcessor which reacts to various highlight info lifecycle events - HighlightVisitorProducer which allows to change highlight visitor set to run in this pass (needed in DefaultHighlightVisitorBasedInspection) All this allows to get rid of MessageBus-propagated daemon events which turned out to be too expensive and to untangle GHP from UpdateHighlightingUtil. df5526b: moved to indexing 6581043: cleanup e68c984: moved to project-model 48c7973: suggest to create a file when user creates a directory named "foo.xt" (IDEA-113072) 85fd323: IDEA-112499 Static imports in live templates fail the second time they're used 95693da: WEB-9248 Emmet fails when having HTML entities 27c1f03: un-inject language 167b44d: un-inject language 22e526c: fix documentation prvovider 897e8ea6: resource roots: suggest only java roots for java-specific actions 64055b1: resource roots: show 'out of source' icon for java files under resource roots 5c9daf7: unused method removed 0f17e3d: file chooser: convert jar file from local FS to jar FS in all cases (IDEA-113159) 0b8e31f: <with> for changesViewContent 7349329: suppress warning for xmlRpcHandler 3dd899a: <with> for psi.referenceProvider, quoteHandler 4b0a964: revert 01d5b0e aa2399b: Merge branch 'svn1_8_new' 05ab0c6: DevKit: fix type name for plugin references f7c6cdb: DevKit: no do spellcheck <vendor> 5dc41cb: IDEA-94942 Implemented "Relocate" action 835d71e: java: utility method extracted 79f9b398: remove LoggerFactory.getInstance() because it can silently override static Log4j properties even when TestLoggerFactory is used e6cfd25: IDEA-112456 Implemented "Cleanup" action 2e9d443: Refresh $APP_ROOT/plugins manually on Grails application synchronization. +review CR-IU-291 7c2343c: Nullness cannot be null, therefore mark nullness as @NotNull 8d7f95a: IDEA-26350 (Bad Java code is green: variable might not have been initialized) 7ec8c9e: cleanup 0f63921: EA-49491 - NPE: RenameFileFix.isAvailable b0fb4e9: EA-49640 - IAE: ActionManagerImpl.getId a48e1e6: more diagnostics ed10a0e: more diagnostics c6190a8: cleanup de46935: IDEA-113147 Fixed "typo" so that url field contains url value (but not depth value) 7bf4b24: compact ui mode 842c947: IDEA-113180 Force '@' at the end of paths without peg revision passed to svn command line client ab23b28: don't generate java doc for synthetic elements 26ae6ba: Lense mode "internal" preview e1bb3a2: EA-48054 - CCE: MethodResolverProcessor.execute af608e8: EA-49203 - ISE: GrAnonymousClassType.getJavaClassName a62b14d: EA-48178 - IAE: GrLightClassReferenceElement.<init> b93e88c: EA-49152 - diagnostics ec324a7: Add synchronization on initialization of GroovyMethodInfo +review CR-IC @gromopert f98f472: AppCode: canceling simulator launch, if there are pending sessions to start b54680b: IDEA-94942 Refactored command line commit logic to use common way of parameters creation 8b0c612: remove GroovyDeclarationSearcher as it's only needed for dead Groovy++ (EA-49626) c608b03: dfa: use human numeration, not programmers' one 0c2794b: remove alphanumbering from Reopen Recent Project action 857fb3d: enable "scroll to the end" console action when the last line is not visible (IDEA-113322) 43a124a: correct implementation of ReverseCharSequence.subSequence 1337e5f: WEB-9232 Debugger: support inline base64-encoded source maps: nodejs support 4a0d7d9: AppServerSupportConfigurable reworked afe88ea: reworked IO exception dialog (IDEA-103128) 641595d: artifact editor: keep output file name and artifact name in sync even if output directory name was changed (IDEA-113412) 2a99cd3: IDEA-103015: Project Structure / module / Paths / output paths: Module action in disabled in file choosers ab8aa4e: EA-49739 added null checks in JDParser#formatJDTagDescription [CR-IC-2248] 668cf2c: IDEA-113465 Groovy: UOE at GrConcatenationAwareInjector.getLanguagesToInject() on adding @Language annotation to a string 9a8589c: EA-49261 - assert: PsiElementBase.notNullChild 5a99120: don't generate javadoc for light elements c8d30b0: EA-49487 - diagnostics ccf2ac3: Groovy introduce refactorings: show error hint 878ec43: EA-49641 - IAE: MissingReturnInspection.methodMissesSomeReturns 9574118: EA-47298 - IOE: PsiJavaParserFacadeImpl.createTypeElementFromText 4fceec4: use xml injections in Groovy 1775e6f: Fix maven test 9c4df88: IDEA-94942 Removed fake "empty" svn command 3c76855: IDEA-94942 Refactored status command not to use fake "empty" svn command e0556d8: IDEA-112811 Create client factory for merge based on target working copy format 1b27b17: IDEA-112811 Refactored merge options creation 243ba78: Allow inheritance from HierarchyBrowser implementations (Type/Method hierarchy) 91ef206: IDEA-112803 Implemented tree conflict resolution in "change details" panel 8abd924: IDEA-94942 Add ability to specify depth for "resolve conflict" command 967205e: IDEA-112803 Fixed committed data parsing for "svn status" command 12854d7: IDEA-94942 Update logic refactored to ClientFactory model 4a16e4b: IDEA-94942 Code cleanup - removed unused imports Change-Id: I6acefa9563b98b26db1f88e21354a0de20c27770
Diffstat (limited to 'java/java-psi-impl/src/com/intellij/psi')
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java31
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java4
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java10
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/InferenceGraphNode.java143
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java5
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiOldInferenceHelper.java1233
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java1192
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceBound.java25
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java187
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java333
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java72
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariablesOrder.java184
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiGraphInferenceHelper.java78
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java38
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ConstraintFormula.java27
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java107
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java75
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/SubtypingConstraint.java175
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java81
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java124
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java244
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java2
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java3
23 files changed, 3031 insertions, 1342 deletions
diff --git a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java
index 88440552b4ca..ca2539195ee2 100644
--- a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java
+++ b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java
@@ -23,7 +23,7 @@ import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
-import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
+import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
@@ -130,7 +130,7 @@ public class PsiDiamondTypeImpl extends PsiDiamondType {
final PsiExpressionList argumentList = newExpression.getArgumentList();
if (argumentList == null) return DiamondInferenceResult.NULL_RESULT;
final Ref<PsiMethod> staticFactoryRef = new Ref<PsiMethod>();
- final PsiSubstitutor inferredSubstitutor = ourDiamondGuard.doPreventingRecursion(newExpression, false, new Computable<PsiSubstitutor>() {
+ final PsiSubstitutor inferredSubstitutor = ourDiamondGuard.doPreventingRecursion(context, false, new Computable<PsiSubstitutor>() {
@Override
public PsiSubstitutor compute() {
final PsiMethod constructor = findConstructor(psiClass, newExpression);
@@ -268,14 +268,27 @@ public class PsiDiamondTypeImpl extends PsiDiamondType {
private static PsiSubstitutor inferTypeParametersForStaticFactory(@NotNull PsiMethod staticFactoryMethod,
PsiNewExpression expression,
- PsiElement parent) {
- final JavaPsiFacade facade = JavaPsiFacade.getInstance(staticFactoryMethod.getProject());
- final PsiResolveHelper resolveHelper = facade.getResolveHelper();
- final PsiParameter[] parameters = staticFactoryMethod.getParameterList().getParameters();
+ final PsiElement parent) {
final PsiExpressionList argumentList = expression.getArgumentList();
- final PsiExpression[] expressions = argumentList.getExpressions();
- return resolveHelper
- .inferTypeArguments(staticFactoryMethod.getTypeParameters(), parameters, expressions, PsiSubstitutor.EMPTY, parent, DefaultParameterTypeInferencePolicy.INSTANCE);
+ if (argumentList != null) {
+ final MethodCandidateInfo staticFactoryCandidateInfo =
+ new MethodCandidateInfo(staticFactoryMethod, PsiSubstitutor.EMPTY, false, false, argumentList, parent,
+ argumentList.getExpressionTypes(), null) {
+ @Override
+ protected PsiElement getParent() {
+ return parent;
+ }
+
+ @Override
+ protected PsiElement getMarkerList() {
+ return parent instanceof PsiNewExpression ? ((PsiNewExpression)parent).getArgumentList() : super.getMarkerList();
+ }
+ };
+ return staticFactoryCandidateInfo.getSubstitutor();
+ }
+ else {
+ return PsiSubstitutor.EMPTY;
+ }
}
public static boolean hasDefaultConstructor(@NotNull final PsiClass psiClass) {
diff --git a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java
index c2675894986d..da3411af8ed6 100644
--- a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java
+++ b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java
@@ -1504,7 +1504,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
startElement(expression);
IElementType op = expression.getOperationTokenType();
- PsiExpression operand = expression.getOperand();
+ PsiExpression operand = PsiUtil.skipParenthesizedExprDown(expression.getOperand());
operand.accept(this);
if (op == JavaTokenType.PLUSPLUS || op == JavaTokenType.MINUSMINUS) {
if (operand instanceof PsiReferenceExpression) {
@@ -1521,7 +1521,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
@Override public void visitPrefixExpression(PsiPrefixExpression expression) {
startElement(expression);
- PsiExpression operand = expression.getOperand();
+ PsiExpression operand = PsiUtil.skipParenthesizedExprDown(expression.getOperand());
if (operand != null) {
IElementType operationSign = expression.getOperationTokenType();
if (operationSign == JavaTokenType.EXCL) {
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java
index b805ef4cf8f0..07f275817a7c 100644
--- a/java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * 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.
@@ -413,21 +413,15 @@ public class JavaPsiFacadeImpl extends JavaPsiFacadeEx {
}
}
-
@Override
public boolean isPartOfPackagePrefix(@NotNull String packageName) {
final Collection<String> packagePrefixes = myFileManager.getNonTrivialPackagePrefixes();
for (final String subpackageName : packagePrefixes) {
- if (isSubpackageOf(subpackageName, packageName)) return true;
+ if (PsiNameHelper.isSubpackageOf(subpackageName, packageName)) return true;
}
return false;
}
- private static boolean isSubpackageOf(@NotNull String subpackageName, @NotNull String packageName) {
- return subpackageName.equals(packageName) ||
- subpackageName.startsWith(packageName) && subpackageName.charAt(packageName.length()) == '.';
- }
-
@Override
public boolean isInPackage(@NotNull PsiElement element, @NotNull PsiPackage aPackage) {
final PsiFile file = FileContextUtil.getContextFile(element);
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/InferenceGraphNode.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/InferenceGraphNode.java
deleted file mode 100644
index d127d4037a28..000000000000
--- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/InferenceGraphNode.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.psi.impl.source.resolve;
-
-import java.util.*;
-
-/**
- * User: anna
- * Date: 7/4/13
- */
-public class InferenceGraphNode<T> {
- private final List<T> myValue = new ArrayList<T>();
- private Set<InferenceGraphNode<T>> myDependencies = new HashSet<InferenceGraphNode<T>>();
-
- private int index = -1;
- private int lowlink;
-
- public InferenceGraphNode(T value) {
- myValue.add(value);
- }
-
- public List<T> getValue() {
- return myValue;
- }
-
- public Set<InferenceGraphNode<T>> getDependencies() {
- return myDependencies;
- }
-
- public void addDependency(InferenceGraphNode<T> node) {
- myDependencies.add(node);
- }
-
- public static <T> List<List<InferenceGraphNode<T>>> tarjan(Collection<InferenceGraphNode<T>> nodes) {
- final ArrayList<List<InferenceGraphNode<T>>> result = new ArrayList<List<InferenceGraphNode<T>>>();
- final Stack<InferenceGraphNode<T>> currentStack = new Stack<InferenceGraphNode<T>>();
- int index = 0;
- for (InferenceGraphNode<T> node : nodes) {
- if (node.index == -1) {
- index += strongConnect(node, index, currentStack, result);
- }
- }
- return result;
- }
-
- public static <T> ArrayList<InferenceGraphNode<T>> initNodes(Collection<InferenceGraphNode<T>> allNodes) {
- final List<List<InferenceGraphNode<T>>> nodes = tarjan(allNodes);
- final ArrayList<InferenceGraphNode<T>> acyclicNodes = new ArrayList<InferenceGraphNode<T>>();
- for (List<InferenceGraphNode<T>> cycle : nodes) {
- acyclicNodes.add(merge(cycle, allNodes));
- }
- return acyclicNodes;
- }
-
- private static <T> InferenceGraphNode<T> merge(final List<InferenceGraphNode<T>> cycle,
- final Collection<InferenceGraphNode<T>> allNodes) {
- assert !cycle.isEmpty();
- final InferenceGraphNode<T> root = cycle.get(0);
- if (cycle.size() > 1) {
- for (int i = 1; i < cycle.size(); i++) {
- final InferenceGraphNode<T> cycleNode = cycle.get(i);
-
- root.copyFrom(cycleNode);
- root.filterInterCycleDependencies();
-
- for (InferenceGraphNode<T> node : allNodes) {
- if (node.myDependencies.remove(cycleNode)) {
- node.myDependencies.add(root);
- }
- }
- }
- }
- return root;
- }
-
- private void filterInterCycleDependencies() {
- boolean includeSelfDependency = false;
- for (Iterator<InferenceGraphNode<T>> iterator = myDependencies.iterator(); iterator.hasNext(); ) {
- InferenceGraphNode<T> d = iterator.next();
- assert d.myValue.size() >= 1;
- final T initialNodeValue = d.myValue.get(0);
- if (myValue.contains(initialNodeValue)) {
- includeSelfDependency = true;
- iterator.remove();
- }
- }
-
- if (includeSelfDependency) {
- myDependencies.add(this);
- }
- }
-
- private void copyFrom(final InferenceGraphNode<T> cycleNode) {
- myValue.addAll(cycleNode.myValue);
- myDependencies.addAll(cycleNode.myDependencies);
- }
-
- private static <T> int strongConnect(InferenceGraphNode<T> currentNode,
- int index,
- Stack<InferenceGraphNode<T>> currentStack,
- ArrayList<List<InferenceGraphNode<T>>> result) {
- currentNode.index = index;
- currentNode.lowlink = index;
- index++;
-
- currentStack.push(currentNode);
-
- for (InferenceGraphNode<T> dependantNode : currentNode.getDependencies()) {
- if (dependantNode.index == -1) {
- strongConnect(dependantNode, index, currentStack, result);
- currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.lowlink);
- }
- else if (currentStack.contains(dependantNode)) {
- currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.index);
- }
- }
-
- if (currentNode.lowlink == currentNode.index) {
- final ArrayList<InferenceGraphNode<T>> arrayList = new ArrayList<InferenceGraphNode<T>>();
- InferenceGraphNode<T> cyclicNode;
- do {
- cyclicNode = currentStack.pop();
- arrayList.add(cyclicNode);
- }
- while (cyclicNode != currentNode);
- result.add(arrayList);
- }
- return index;
- }
-}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java
index 1b33cfb196b6..e88fca2a3025 100644
--- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java
@@ -17,6 +17,7 @@ package com.intellij.psi.impl.source.resolve;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
+import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.MethodProcessorSetupFailedException;
@@ -109,9 +110,9 @@ public class ProcessCandidateParameterTypeInferencePolicy extends DefaultParamet
}
});
PsiResolveHelperImpl resolveHelper = (PsiResolveHelperImpl)JavaPsiFacade.getInstance(method.getProject()).getResolveHelper();
+ final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(finalParameter);
final Pair<PsiType, ConstraintType> constraint =
- resolveHelper.getSubstitutionForTypeParameterConstraint(typeParameter, innerReturnType, type, false,
- PsiUtil.getLanguageLevel(finalParameter));
+ ((PsiOldInferenceHelper)resolveHelper.getInferenceHelper(languageLevel)).getSubstitutionForTypeParameterConstraint(typeParameter, innerReturnType, type, false, languageLevel);
if (constraint != null) return constraint;
}
}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiOldInferenceHelper.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiOldInferenceHelper.java
new file mode 100644
index 000000000000..8615d912354c
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiOldInferenceHelper.java
@@ -0,0 +1,1233 @@
+/*
+ * 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.psi.impl.source.resolve;
+
+import com.intellij.codeInsight.ExceptionUtil;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdkVersion;
+import com.intellij.openapi.projectRoots.JavaVersionService;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.RecursionGuard;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
+import com.intellij.psi.infos.MethodCandidateInfo;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.*;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.HashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * User: anna
+ */
+public class PsiOldInferenceHelper implements PsiInferenceHelper {
+ private static final Logger LOG = Logger.getInstance("#" + PsiOldInferenceHelper.class.getName());
+ public static final Pair<PsiType,ConstraintType> RAW_INFERENCE = new Pair<PsiType, ConstraintType>(null, ConstraintType.EQUALS);
+ private final PsiManager myManager;
+
+ public PsiOldInferenceHelper(PsiManager manager) {
+ myManager = manager;
+ }
+
+ private Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(@NotNull PsiTypeParameter typeParameter,
+ @NotNull PsiParameter[] parameters,
+ @NotNull PsiExpression[] arguments,
+ @NotNull PsiSubstitutor partialSubstitutor,
+ final PsiElement parent,
+ @NotNull ParameterTypeInferencePolicy policy) {
+ PsiType[] paramTypes = new PsiType[arguments.length];
+ PsiType[] argTypes = new PsiType[arguments.length];
+ if (parameters.length > 0) {
+ for (int j = 0; j < argTypes.length; j++) {
+ final PsiExpression argument = arguments[j];
+ if (argument == null) continue;
+ if (argument instanceof PsiMethodCallExpression && PsiResolveHelper.ourGuard.currentStack().contains(argument)) continue;
+
+ final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack();
+ argTypes[j] = argument.getType();
+ if (!stackStamp.mayCacheNow()) {
+ argTypes[j] = null;
+ continue;
+ }
+
+ final PsiParameter parameter = parameters[Math.min(j, parameters.length - 1)];
+ if (j >= parameters.length && !parameter.isVarArgs()) break;
+ paramTypes[j] = parameter.getType();
+ if (paramTypes[j] instanceof PsiEllipsisType) {
+ paramTypes[j] = ((PsiEllipsisType)paramTypes[j]).getComponentType();
+ if (arguments.length == parameters.length &&
+ argTypes[j] instanceof PsiArrayType &&
+ !(((PsiArrayType)argTypes[j]).getComponentType() instanceof PsiPrimitiveType)) {
+ argTypes[j] = ((PsiArrayType)argTypes[j]).getComponentType();
+ }
+ }
+ }
+ }
+ return inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, argTypes, partialSubstitutor, parent, policy);
+ }
+
+ private Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(@NotNull PsiTypeParameter typeParameter,
+ @NotNull PsiType[] paramTypes,
+ @NotNull PsiType[] argTypes,
+ @NotNull PsiSubstitutor partialSubstitutor,
+ @Nullable PsiElement parent,
+ @NotNull ParameterTypeInferencePolicy policy) {
+ PsiWildcardType wildcardToCapture = null;
+ Pair<PsiType, ConstraintType> rawInference = null;
+ PsiType lowerBound = PsiType.NULL;
+ PsiType upperBound = PsiType.NULL;
+ if (paramTypes.length > 0) {
+ sortLambdaExpressionsLast(paramTypes, argTypes);
+ boolean rawType = false;
+ boolean nullPassed = false;
+ boolean lambdaRaw = false;
+ for (int j = 0; j < argTypes.length; j++) {
+ PsiType argumentType = argTypes[j];
+ if (argumentType == null) continue;
+ if (j >= paramTypes.length) break;
+
+ PsiType parameterType = paramTypes[j];
+ if (parameterType == null) break;
+ rawType |= parameterType instanceof PsiClassType && ((PsiClassType)parameterType).isRaw();
+ nullPassed |= argumentType == PsiType.NULL;
+
+ if (parameterType instanceof PsiEllipsisType) {
+ parameterType = ((PsiEllipsisType)parameterType).getComponentType();
+ if (argTypes.length == paramTypes.length && argumentType instanceof PsiArrayType && !(((PsiArrayType)argumentType).getComponentType() instanceof PsiPrimitiveType)) {
+ argumentType = ((PsiArrayType)argumentType).getComponentType();
+ }
+ }
+ final Pair<PsiType,ConstraintType> currentSubstitution;
+ if (argumentType instanceof PsiLambdaExpressionType) {
+ currentSubstitution = inferSubstitutionFromLambda(typeParameter, (PsiLambdaExpressionType)argumentType, lowerBound, partialSubstitutor);
+ if (rawType) {
+ if (currentSubstitution == FAILED_INFERENCE || currentSubstitution == null && lowerBound == PsiType.NULL) return RAW_INFERENCE;
+ }
+ if (nullPassed && currentSubstitution == null) return RAW_INFERENCE;
+ if (currentSubstitution != null && currentSubstitution.first == null) {
+ lambdaRaw = true;
+ }
+ if (currentSubstitution == null && lambdaRaw) {
+ return new Pair<PsiType, ConstraintType>(PsiType.getJavaLangObject(myManager, typeParameter.getResolveScope()), ConstraintType.EQUALS);
+ }
+ } else if (argumentType instanceof PsiMethodReferenceType) {
+ final PsiMethodReferenceExpression referenceExpression = ((PsiMethodReferenceType)argumentType).getExpression();
+ currentSubstitution = inferConstraintFromFunctionalInterfaceMethod(typeParameter, referenceExpression, partialSubstitutor.substitute(parameterType), partialSubstitutor, policy);
+ }
+ else {
+ currentSubstitution = getSubstitutionForTypeParameterConstraint(typeParameter, parameterType,
+ argumentType, true, PsiUtil.getLanguageLevel(typeParameter));
+ }
+ if (currentSubstitution == null) continue;
+ if (currentSubstitution == FAILED_INFERENCE) {
+ return getFailedInferenceConstraint(typeParameter);
+ }
+
+ final ConstraintType constraintType = currentSubstitution.getSecond();
+ final PsiType type = currentSubstitution.getFirst();
+ if (type == null) {
+ rawInference = RAW_INFERENCE;
+ continue;
+ }
+ switch(constraintType) {
+ case EQUALS:
+ if (!(type instanceof PsiWildcardType)) return currentSubstitution;
+ if (wildcardToCapture != null) return getFailedInferenceConstraint(typeParameter);
+ wildcardToCapture = (PsiWildcardType) type;
+ break;
+ case SUPERTYPE:
+ if (PsiType.NULL.equals(lowerBound)) {
+ lowerBound = type;
+ }
+ else if (!lowerBound.equals(type)) {
+ lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, type, myManager);
+ if (lowerBound == null) return getFailedInferenceConstraint(typeParameter);
+ }
+ break;
+ case SUBTYPE:
+ if (PsiType.NULL.equals(upperBound) || TypeConversionUtil.isAssignable(upperBound, type)) {
+ upperBound = type;
+ }
+ }
+ }
+ }
+
+ if (wildcardToCapture != null) {
+ if (lowerBound != PsiType.NULL) {
+ if (!wildcardToCapture.isAssignableFrom(lowerBound)) return getFailedInferenceConstraint(typeParameter);
+ if (wildcardToCapture.isSuper()) {
+ return new Pair<PsiType, ConstraintType>(wildcardToCapture, ConstraintType.SUPERTYPE);
+ }
+ lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, wildcardToCapture, myManager);
+ }
+ else {
+ if (upperBound != PsiType.NULL && !upperBound.isAssignableFrom(wildcardToCapture)) return getFailedInferenceConstraint(typeParameter);
+ return new Pair<PsiType, ConstraintType>(wildcardToCapture, ConstraintType.EQUALS);
+ }
+ }
+
+ if (rawInference != null) return rawInference;
+ if (lowerBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(lowerBound, ConstraintType.EQUALS);
+
+ if (parent != null) {
+ final Pair<PsiType, ConstraintType> constraint =
+ inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy);
+ if (constraint != null) {
+ if (constraint.getSecond() != ConstraintType.SUBTYPE) {
+ return constraint;
+ }
+
+ if (upperBound != PsiType.NULL) {
+ return new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE);
+ }
+
+ return constraint;
+ }
+ }
+
+ if (upperBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE);
+ return null;
+ }
+
+ private static void sortLambdaExpressionsLast(@NotNull PsiType[] paramTypes, @NotNull PsiType[] argTypes) {
+ for (int i = 0; i < argTypes.length; i++) {
+ PsiType argType = argTypes[i];
+ if ((argType instanceof PsiLambdaExpressionType || argType instanceof PsiMethodReferenceType) && i < argTypes.length - 1) {
+ int k = i + 1;
+ while((argTypes[k] instanceof PsiLambdaExpressionType || argTypes[k] instanceof PsiMethodReferenceType) && k < argTypes.length - 1) {
+ k++;
+ }
+ if (!(argTypes[k] instanceof PsiLambdaExpressionType || argTypes[k] instanceof PsiMethodReferenceType)) {
+ ArrayUtil.swap(paramTypes, i, k);
+ ArrayUtil.swap(argTypes, i, k);
+ i = k;
+ }
+ }
+ }
+ }
+
+ private static Pair<PsiType, ConstraintType> getFailedInferenceConstraint(@NotNull PsiTypeParameter typeParameter) {
+ return new Pair<PsiType, ConstraintType>(JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(typeParameter), ConstraintType.EQUALS);
+ }
+
+ @Override
+ public PsiType inferTypeForMethodTypeParameter(@NotNull final PsiTypeParameter typeParameter,
+ @NotNull final PsiParameter[] parameters,
+ @NotNull PsiExpression[] arguments,
+ @NotNull PsiSubstitutor partialSubstitutor,
+ PsiElement parent,
+ @NotNull final ParameterTypeInferencePolicy policy) {
+
+ final Pair<PsiType, ConstraintType> constraint =
+ inferTypeForMethodTypeParameterInner(typeParameter, parameters, arguments, partialSubstitutor, parent, policy);
+ if (constraint == null) return PsiType.NULL;
+ return constraint.getFirst();
+ }
+
+ @NotNull
+ @Override
+ public PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters,
+ @NotNull PsiParameter[] parameters,
+ @NotNull PsiExpression[] arguments,
+ @NotNull PsiSubstitutor partialSubstitutor,
+ @NotNull PsiElement parent,
+ @NotNull ParameterTypeInferencePolicy policy,
+ @NotNull LanguageLevel languageLevel) {
+ PsiType[] substitutions = new PsiType[typeParameters.length];
+ @SuppressWarnings("unchecked")
+ Pair<PsiType, ConstraintType>[] constraints = new Pair[typeParameters.length];
+ for (int i = 0; i < typeParameters.length; i++) {
+ if (substitutions[i] != null) continue;
+ final Pair<PsiType, ConstraintType> constraint =
+ inferTypeForMethodTypeParameterInner(typeParameters[i], parameters, arguments, partialSubstitutor, null, policy);
+ constraints[i] = constraint;
+ if (constraint != null && constraint.getSecond() != ConstraintType.SUBTYPE) {
+ substitutions[i] = constraint.getFirst();
+
+ if (substitutions[i] != null && languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { //try once more
+ partialSubstitutor = partialSubstitutor.put(typeParameters[i], substitutions[i]);
+ i = -1;
+ }
+ }
+ }
+
+ for (int i = 0; i < typeParameters.length; i++) {
+ PsiTypeParameter typeParameter = typeParameters[i];
+ if (substitutions[i] == null) {
+ PsiType substitutionFromBounds = PsiType.NULL;
+ OtherParameters:
+ for (int j = 0; j < typeParameters.length; j++) {
+ if (i != j) {
+ PsiTypeParameter other = typeParameters[j];
+ final PsiType otherSubstitution = substitutions[j];
+ if (otherSubstitution == null) continue;
+ final PsiClassType[] bounds = other.getExtendsListTypes();
+ for (PsiClassType bound : bounds) {
+ final PsiType substitutedBound = partialSubstitutor.substitute(bound);
+ final Pair<PsiType, ConstraintType> currentConstraint =
+ getSubstitutionForTypeParameterConstraint(typeParameter, substitutedBound, otherSubstitution, true, languageLevel);
+ if (currentConstraint == null) continue;
+ final PsiType currentSubstitution = currentConstraint.getFirst();
+ final ConstraintType currentConstraintType = currentConstraint.getSecond();
+ if (currentConstraintType == ConstraintType.EQUALS) {
+ substitutionFromBounds = currentSubstitution;
+ if (currentSubstitution == null) {
+ constraints[i] = FAILED_INFERENCE;
+ }
+ break OtherParameters;
+ }
+ else if (currentConstraintType == ConstraintType.SUPERTYPE) {
+ if (PsiType.NULL.equals(substitutionFromBounds)) {
+ substitutionFromBounds = currentSubstitution;
+ }
+ else {
+ substitutionFromBounds = GenericsUtil.getLeastUpperBound(substitutionFromBounds, currentSubstitution, myManager);
+ }
+ }
+ }
+
+ }
+ }
+
+ if (substitutionFromBounds != PsiType.NULL) substitutions[i] = substitutionFromBounds;
+ }
+ }
+
+ for (int i = 0; i < typeParameters.length; i++) {
+ PsiTypeParameter typeParameter = typeParameters[i];
+ PsiType substitution = substitutions[i];
+ if (substitution != PsiType.NULL) {
+ partialSubstitutor = partialSubstitutor.put(typeParameter, substitution);
+ }
+ }
+
+ try {
+ for (int i = 0; i < typeParameters.length; i++) {
+ PsiTypeParameter typeParameter = typeParameters[i];
+ PsiType substitution = substitutions[i];
+ if (substitution != null) continue;
+
+ Pair<PsiType, ConstraintType> constraint = constraints[i];
+ if (constraint == null) {
+ constraint = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy);
+ }
+ else if (constraint.getSecond() == ConstraintType.SUBTYPE) {
+ Pair<PsiType, ConstraintType> otherConstraint = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy);
+ if (otherConstraint != null) {
+ if (otherConstraint.getSecond() == ConstraintType.EQUALS || otherConstraint.getSecond() == ConstraintType.SUPERTYPE) {
+ constraint = otherConstraint;
+ }
+ }
+ }
+
+ if (constraint != null) {
+ substitution = constraint.getFirst();
+ }
+
+ if (substitution == null) {
+ PsiElementFactory factory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
+ return factory.createRawSubstitutor(partialSubstitutor, typeParameters);
+ }
+ if (substitution != PsiType.NULL) {
+ partialSubstitutor = partialSubstitutor.put(typeParameter, substitution);
+ }
+ }
+ }
+ finally {
+ GraphInferencePolicy.forget(parent);
+ }
+ return partialSubstitutor;
+ }
+
+ @Override
+ @NotNull
+ public PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters,
+ @NotNull PsiType[] leftTypes,
+ @NotNull PsiType[] rightTypes,
+ @NotNull LanguageLevel languageLevel) {
+ if (leftTypes.length != rightTypes.length) throw new IllegalArgumentException("Types must be of the same length");
+ PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
+ for (PsiTypeParameter typeParameter : typeParameters) {
+ PsiType substitution = PsiType.NULL;
+ PsiType lowerBound = PsiType.NULL;
+ for (int i1 = 0; i1 < leftTypes.length; i1++) {
+ PsiType leftType = leftTypes[i1];
+ PsiType rightType = rightTypes[i1];
+ final Pair<PsiType, ConstraintType> constraint =
+ getSubstitutionForTypeParameterConstraint(typeParameter, leftType, rightType, true, languageLevel);
+ if (constraint != null) {
+ final ConstraintType constraintType = constraint.getSecond();
+ final PsiType current = constraint.getFirst();
+ if (constraintType == ConstraintType.EQUALS) {
+ substitution = current;
+ break;
+ }
+ else if (constraintType == ConstraintType.SUBTYPE) {
+ if (PsiType.NULL.equals(substitution)) {
+ substitution = current;
+ }
+ else {
+ substitution = GenericsUtil.getLeastUpperBound(substitution, current, myManager);
+ }
+ }
+ else {
+ if (PsiType.NULL.equals(lowerBound)) {
+ lowerBound = current;
+ }
+ else {
+ lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, current, myManager);
+ }
+ }
+ }
+ }
+
+ if (PsiType.NULL.equals(substitution)) {
+ substitution = lowerBound;
+ }
+
+ if (substitution != PsiType.NULL) {
+ substitutor = substitutor.put(typeParameter, substitution);
+ }
+ }
+ for (int i = 0; i < typeParameters.length; i++) {
+ PsiTypeParameter typeParameter = typeParameters[i];
+ if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
+ PsiType substitutionFromBounds = PsiType.NULL;
+ OtherParameters:
+ for (int j = 0; j < typeParameters.length; j++) {
+ if (i != j) {
+ PsiTypeParameter other = typeParameters[j];
+ final PsiType otherSubstitution = substitutor.substitute(other);
+ if (otherSubstitution == null) continue;
+ final PsiClassType[] bounds = other.getExtendsListTypes();
+ for (PsiClassType bound : bounds) {
+ final PsiType substitutedBound = substitutor.substitute(bound);
+ final Pair<PsiType, ConstraintType> currentConstraint =
+ getSubstitutionForTypeParameterConstraint(typeParameter, substitutedBound, otherSubstitution, true, languageLevel);
+ if (currentConstraint == null) continue;
+ final PsiType currentSubstitution = currentConstraint.getFirst();
+ final ConstraintType currentConstraintType = currentConstraint.getSecond();
+ if (currentConstraintType == ConstraintType.EQUALS) {
+ substitutionFromBounds = currentSubstitution;
+ break OtherParameters;
+ }
+ else if (currentConstraintType == ConstraintType.SUPERTYPE) {
+ if (PsiType.NULL.equals(substitutionFromBounds)) {
+ substitutionFromBounds = currentSubstitution;
+ }
+ else {
+ substitutionFromBounds = GenericsUtil.getLeastUpperBound(substitutionFromBounds, currentSubstitution, myManager);
+ }
+ }
+ }
+ }
+ }
+ if (substitutionFromBounds != PsiType.NULL) {
+ substitutor = substitutor.put(typeParameter, substitutionFromBounds);
+ }
+ }
+ }
+ return substitutor;
+ }
+
+ @Nullable
+ private static Pair<PsiType, ConstraintType> processArgType(PsiType arg, final ConstraintType constraintType,
+ final boolean captureWildcard) {
+ if (arg instanceof PsiWildcardType && !captureWildcard) return FAILED_INFERENCE;
+ if (arg != PsiType.NULL) {
+ return new Pair<PsiType, ConstraintType>(arg, constraintType);
+ }
+ return null;
+ }
+
+ private Pair<PsiType, ConstraintType> inferMethodTypeParameterFromParent(@NotNull PsiTypeParameter typeParameter,
+ @NotNull PsiSubstitutor substitutor,
+ @NotNull PsiElement parent,
+ @NotNull ParameterTypeInferencePolicy policy) {
+ PsiTypeParameterListOwner owner = typeParameter.getOwner();
+ Pair<PsiType, ConstraintType> substitution = null;
+ if (owner instanceof PsiMethod && parent instanceof PsiCallExpression) {
+ PsiCallExpression methodCall = (PsiCallExpression)parent;
+ substitution = inferMethodTypeParameterFromParent(PsiUtil.skipParenthesizedExprUp(methodCall.getParent()), methodCall, typeParameter, substitutor, policy);
+ }
+ return substitution;
+ }
+
+ @Override
+ public PsiType getSubstitutionForTypeParameter(PsiTypeParameter typeParam,
+ PsiType param,
+ PsiType arg,
+ boolean isContraVariantPosition,
+ final LanguageLevel languageLevel) {
+ final Pair<PsiType, ConstraintType> constraint = getSubstitutionForTypeParameterConstraint(typeParam, param, arg, isContraVariantPosition,
+ languageLevel);
+ return constraint == null ? PsiType.NULL : constraint.getFirst();
+ }
+
+ @Nullable
+ public Pair<PsiType, ConstraintType> getSubstitutionForTypeParameterConstraint(PsiTypeParameter typeParam,
+ PsiType param,
+ PsiType arg,
+ boolean isContraVariantPosition,
+ final LanguageLevel languageLevel) {
+ if (param instanceof PsiArrayType && arg instanceof PsiArrayType) {
+ return getSubstitutionForTypeParameterConstraint(typeParam, ((PsiArrayType)param).getComponentType(), ((PsiArrayType)arg).getComponentType(),
+ isContraVariantPosition, languageLevel);
+ }
+
+ if (!(param instanceof PsiClassType)) return null;
+ PsiManager manager = myManager;
+ if (arg instanceof PsiPrimitiveType) {
+ if (!JavaVersionService.getInstance().isAtLeast(typeParam, JavaSdkVersion.JDK_1_7) && !isContraVariantPosition) return null;
+ arg = ((PsiPrimitiveType)arg).getBoxedType(typeParam);
+ if (arg == null) return null;
+ }
+
+ JavaResolveResult paramResult = ((PsiClassType)param).resolveGenerics();
+ PsiClass paramClass = (PsiClass)paramResult.getElement();
+ if (typeParam == paramClass) {
+ final PsiClass psiClass = PsiUtil.resolveClassInType(arg);
+ if (arg == null ||
+ arg.getDeepComponentType() instanceof PsiPrimitiveType ||
+ arg instanceof PsiIntersectionType ||
+ (psiClass != null && (isContraVariantPosition || !CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName()) || (arg instanceof PsiArrayType)))) {
+ PsiType bound = intersectAllExtends(typeParam, arg);
+ return new Pair<PsiType, ConstraintType>(bound, ConstraintType.SUPERTYPE);
+ }
+ if (psiClass == null && arg instanceof PsiClassType) {
+ return Pair.create(arg, ConstraintType.EQUALS);
+ }
+ return null;
+ }
+ if (paramClass == null) return null;
+
+ if (!(arg instanceof PsiClassType)) return null;
+
+ JavaResolveResult argResult = ((PsiClassType)arg).resolveGenerics();
+ PsiClass argClass = (PsiClass)argResult.getElement();
+ if (argClass == null) return null;
+
+ PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
+ PsiType patternType = factory.createType(typeParam);
+ if (isContraVariantPosition) {
+ PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(paramClass, argClass, argResult.getSubstitutor());
+ if (substitutor == null) return null;
+ arg = factory.createType(paramClass, substitutor, languageLevel);
+ }
+ else {
+ PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(argClass, paramClass, paramResult.getSubstitutor());
+ if (substitutor == null) return null;
+ param = factory.createType(argClass, substitutor, languageLevel);
+ }
+
+ return getSubstitutionForTypeParameterInner(param, arg, patternType, ConstraintType.SUPERTYPE, 0);
+ }
+
+ @Nullable
+ private Pair<PsiType, ConstraintType> inferSubstitutionFromLambda(PsiTypeParameter typeParam,
+ PsiLambdaExpressionType arg,
+ PsiType lowerBound,
+ PsiSubstitutor partialSubstitutor) {
+ final PsiLambdaExpression lambdaExpression = arg.getExpression();
+ if (PsiUtil.getLanguageLevel(lambdaExpression).isAtLeast(LanguageLevel.JDK_1_8)) {
+ final PsiElement parent = PsiUtil.skipParenthesizedExprUp(lambdaExpression.getParent());
+ if (parent instanceof PsiExpressionList) {
+ final PsiExpressionList expressionList = (PsiExpressionList)parent;
+ final Map<PsiElement, Pair<PsiMethod, PsiSubstitutor>> methodMap = MethodCandidateInfo.CURRENT_CANDIDATE.get();
+ final Pair<PsiMethod, PsiSubstitutor> pair = methodMap != null ? methodMap.get(expressionList) : null;
+ if (pair != null) {
+ final int i = LambdaUtil.getLambdaIdx(expressionList, lambdaExpression);
+ if (i < 0) return null;
+ final PsiParameter[] parameters = pair.first.getParameterList().getParameters();
+ if (parameters.length <= i) return null;
+ final PsiSubstitutor combinedSubst = pair.second.putAll(partialSubstitutor);
+ methodMap.put(expressionList, Pair.create(pair.first, combinedSubst));
+ return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression, combinedSubst.substitute(parameters[i].getType()), lowerBound);
+ }
+ }
+ else {
+ return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression,
+ partialSubstitutor.substitute(lambdaExpression.getFunctionalInterfaceType()), lowerBound);
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(final PsiTypeParameter typeParam,
+ final PsiMethodReferenceExpression methodReferenceExpression,
+ final PsiType functionalInterfaceType,
+ final PsiSubstitutor partialSubstitutor,
+ final ParameterTypeInferencePolicy policy) {
+ final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
+ final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
+ if (functionalInterfaceMethod != null) {
+ final PsiSubstitutor subst = LambdaUtil.getSubstitutor(functionalInterfaceMethod, resolveResult);
+ final PsiParameter[] methodParameters = functionalInterfaceMethod.getParameterList().getParameters();
+ PsiType[] methodParamTypes = new PsiType[methodParameters.length];
+ for (int i = 0; i < methodParameters.length; i++) {
+ methodParamTypes[i] = GenericsUtil.eliminateWildcards(subst.substitute(methodParameters[i].getType()));
+ }
+
+ if (methodParamsDependOn(typeParam, methodReferenceExpression, functionalInterfaceType, methodParameters, subst)) {
+ return null;
+ }
+
+ final PsiType[] args = new PsiType[methodParameters.length];
+ Map<PsiMethodReferenceExpression,PsiType> map = PsiMethodReferenceUtil.ourRefs.get();
+ if (map == null) {
+ map = new HashMap<PsiMethodReferenceExpression, PsiType>();
+ PsiMethodReferenceUtil.ourRefs.set(map);
+ }
+ final PsiType added = map.put(methodReferenceExpression, functionalInterfaceType);
+ final JavaResolveResult methReferenceResolveResult;
+ try {
+ methReferenceResolveResult = methodReferenceExpression.advancedResolve(false);
+ }
+ finally {
+ if (added == null) {
+ map.remove(methodReferenceExpression);
+ }
+ }
+ final PsiElement resolved = methReferenceResolveResult.getElement();
+ if (resolved instanceof PsiMethod) {
+ final PsiMethod method = (PsiMethod)resolved;
+ final PsiParameter[] parameters = method.getParameterList().getParameters();
+ boolean hasReceiver = false;
+ if (methodParamTypes.length == parameters.length + 1) {
+ if (!PsiMethodReferenceUtil
+ .isReceiverType(methodParamTypes[0], method.getContainingClass(), methReferenceResolveResult.getSubstitutor())) return null;
+ hasReceiver = true;
+ } else if (parameters.length != methodParameters.length) {
+ return null;
+ }
+ for (int i = 0; i < parameters.length; i++) {
+ args[i] = methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(parameters[i].getType()));
+ }
+
+ final PsiType[] typesToInfer = hasReceiver ? ArrayUtil.remove(methodParamTypes, 0) : methodParamTypes;
+ final Pair<PsiType, ConstraintType> constraint = inferTypeForMethodTypeParameterInner(typeParam, typesToInfer, args, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE);
+ if (constraint != null){
+ return constraint;
+ }
+ PsiType functionalInterfaceReturnType = functionalInterfaceMethod.getReturnType();
+ if (functionalInterfaceReturnType != null && functionalInterfaceReturnType != PsiType.VOID) {
+ functionalInterfaceReturnType = GenericsUtil.eliminateWildcards(subst.substitute(functionalInterfaceReturnType));
+ final PsiType argType;
+ if (method.isConstructor()) {
+ argType = JavaPsiFacade.getElementFactory(functionalInterfaceMethod.getProject()).createType(method.getContainingClass(), methReferenceResolveResult.getSubstitutor());
+ } else {
+ argType = methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(method.getReturnType()));
+ }
+ final Pair<PsiType, ConstraintType> typeParameterConstraint =
+ getSubstitutionForTypeParameterConstraint(typeParam, functionalInterfaceReturnType, argType, true, PsiUtil.getLanguageLevel(functionalInterfaceMethod));
+ if (typeParameterConstraint != null && typeParameterConstraint.getSecond() != ConstraintType.EQUALS && method.isConstructor()) {
+ final Pair<PsiType, ConstraintType> constraintFromParent =
+ inferMethodTypeParameterFromParent(typeParam, partialSubstitutor, methodReferenceExpression.getParent().getParent(), policy);
+ if (constraintFromParent != null && constraintFromParent.getSecond() == ConstraintType.EQUALS) return constraintFromParent;
+ }
+ return typeParameterConstraint;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(PsiTypeParameter typeParam,
+ final PsiLambdaExpression lambdaExpression,
+ final PsiType functionalInterfaceType,
+ PsiType lowerBound) {
+ final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
+ final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
+ if (method != null) {
+ final PsiSubstitutor subst = LambdaUtil.getSubstitutor(method, resolveResult);
+ final Pair<PsiType, ConstraintType> constraintFromFormalParams = inferConstraintFromLambdaFormalParams(typeParam, subst, method, lambdaExpression);
+ if (constraintFromFormalParams != null) return constraintFromFormalParams;
+
+ final PsiParameter[] methodParameters = method.getParameterList().getParameters();
+ if (methodParamsDependOn(typeParam, lambdaExpression, functionalInterfaceType, methodParameters, subst)) {
+ return null;
+ }
+
+ final PsiType returnType = subst.substitute(method.getReturnType());
+ if (returnType != null && returnType != PsiType.VOID) {
+ Pair<PsiType, ConstraintType> constraint = null;
+ final List<PsiExpression> expressions = LambdaUtil.getReturnExpressions(lambdaExpression);
+ for (final PsiExpression expression : expressions) {
+ final boolean independent = lambdaExpression.hasFormalParameterTypes() || LambdaUtil.isFreeFromTypeInferenceArgs(methodParameters, lambdaExpression, expression, subst, functionalInterfaceType, typeParam);
+ if (!independent) {
+ if (lowerBound != PsiType.NULL) {
+ return null;
+ }
+ continue;
+ }
+ if (expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).resolve() == null) continue;
+ PsiType exprType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, true, new Computable<PsiType>() {
+ @Override
+ public PsiType compute() {
+ return expression.getType();
+ }
+ });
+ if (exprType instanceof PsiLambdaParameterType) {
+ final PsiParameter parameter = ((PsiLambdaParameterType)exprType).getParameter();
+ final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter);
+ if (parameterIndex > -1) {
+ exprType = subst.substitute(methodParameters[parameterIndex].getType());
+ }
+ } else if (exprType instanceof PsiLambdaExpressionType) {
+ return inferConstraintFromFunctionalInterfaceMethod(typeParam, ((PsiLambdaExpressionType)exprType).getExpression(), returnType,
+ lowerBound);
+ } else if (exprType == null && independent) {
+ return null;
+ }
+
+ if (exprType == null){
+ return FAILED_INFERENCE;
+ }
+
+ final Pair<PsiType, ConstraintType> returnExprConstraint =
+ getSubstitutionForTypeParameterConstraint(typeParam, GenericsUtil.eliminateWildcards(returnType), exprType, true, PsiUtil.getLanguageLevel(method));
+ if (returnExprConstraint != null) {
+ if (returnExprConstraint == FAILED_INFERENCE) return returnExprConstraint;
+ if (constraint != null) {
+ final PsiType leastUpperBound = GenericsUtil.getLeastUpperBound(constraint.getFirst(), returnExprConstraint.getFirst(), myManager);
+ constraint = new Pair<PsiType, ConstraintType>(leastUpperBound, ConstraintType.SUPERTYPE);
+ } else {
+ constraint = returnExprConstraint;
+ }
+ }
+ }
+ if (constraint != null) return constraint;
+ }
+ }
+ return null;
+ }
+
+ private static boolean methodParamsDependOn(PsiTypeParameter typeParam, PsiElement psiElement,
+ PsiType functionalInterfaceType,
+ PsiParameter[] methodParameters,
+ PsiSubstitutor subst) {
+ for (PsiParameter parameter : methodParameters) {
+ if (LambdaUtil.dependsOnTypeParams(subst.substitute(parameter.getType()), functionalInterfaceType, psiElement, typeParam)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ private Pair<PsiType, ConstraintType> inferConstraintFromLambdaFormalParams(PsiTypeParameter typeParam,
+ PsiSubstitutor subst,
+ PsiMethod method, PsiLambdaExpression lambdaExpression) {
+ final PsiParameter[] parameters = lambdaExpression.getParameterList().getParameters();
+ if (parameters.length == 0) return null;
+ final PsiType[] lambdaArgs = new PsiType[parameters.length];
+ for (int i = 0; i < parameters.length; i++) {
+ PsiParameter parameter = parameters[i];
+ if (parameter.getTypeElement() == null) {
+ return null;
+ }
+ lambdaArgs[i] = parameter.getType();
+ }
+
+ final PsiParameter[] methodParameters = method.getParameterList().getParameters();
+ PsiType[] methodParamTypes = new PsiType[methodParameters.length];
+ for (int i = 0; i < methodParameters.length; i++) {
+ methodParamTypes[i] = GenericsUtil.eliminateWildcards(subst.substitute(methodParameters[i].getType()));
+ }
+ return inferTypeForMethodTypeParameterInner(typeParam, methodParamTypes, lambdaArgs, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE);
+ }
+
+ private static PsiType intersectAllExtends(PsiTypeParameter typeParam, PsiType arg) {
+ if (arg == null) return null;
+ PsiClassType[] superTypes = typeParam.getSuperTypes();
+ PsiType[] erasureTypes = new PsiType[superTypes.length];
+ for (int i = 0; i < superTypes.length; i++) {
+ erasureTypes[i] = TypeConversionUtil.erasure(superTypes[i]);
+ }
+ PsiType[] types = ArrayUtil.append(erasureTypes, arg, PsiType.class);
+ assert types.length != 0;
+ return PsiIntersectionType.createIntersection(types);
+ }
+
+ //represents the result of failed type inference: in case we failed inferring from parameters, do not perform inference from context
+ private static final Pair<PsiType, ConstraintType> FAILED_INFERENCE = new Pair<PsiType, ConstraintType>(PsiType.NULL, ConstraintType.EQUALS);
+
+ @Nullable
+ private Pair<PsiType, ConstraintType> getSubstitutionForTypeParameterInner(PsiType param,
+ PsiType arg,
+ PsiType patternType,
+ final ConstraintType constraintType,
+ final int depth) {
+ if (arg instanceof PsiCapturedWildcardType && (depth < 2 || constraintType != ConstraintType.EQUALS)) arg = ((PsiCapturedWildcardType)arg).getWildcard(); //reopen
+
+ if (patternType.equals(param)) {
+ return processArgType(arg, constraintType, depth < 2);
+ }
+
+ if (param instanceof PsiWildcardType) {
+ final PsiWildcardType wildcardParam = (PsiWildcardType)param;
+ final PsiType paramBound = wildcardParam.getBound();
+ if (paramBound == null) return null;
+ ConstraintType constrType = wildcardParam.isExtends() ? ConstraintType.SUPERTYPE : ConstraintType.SUBTYPE;
+ if (arg instanceof PsiWildcardType) {
+ if (((PsiWildcardType)arg).isExtends() == wildcardParam.isExtends() && ((PsiWildcardType)arg).isBounded() == wildcardParam.isBounded()) {
+ Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(paramBound, ((PsiWildcardType)arg).getBound(),
+ patternType, constrType, depth);
+ if (res != null) return res;
+ }
+ }
+ else if (patternType.equals(paramBound)) {
+ Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(paramBound, arg,
+ patternType, constrType, depth);
+ if (res != null) return res;
+ }
+ else if (paramBound instanceof PsiArrayType && arg instanceof PsiArrayType) {
+ Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(((PsiArrayType) paramBound).getComponentType(),
+ ((PsiArrayType) arg).getComponentType(),
+ patternType, constrType, depth);
+ if (res != null) return res;
+ }
+ else if (paramBound instanceof PsiClassType && arg instanceof PsiClassType) {
+ final PsiClassType.ClassResolveResult boundResult = ((PsiClassType)paramBound).resolveGenerics();
+ final PsiClass boundClass = boundResult.getElement();
+ if (boundClass != null) {
+ final PsiClassType.ClassResolveResult argResult = ((PsiClassType)arg).resolveGenerics();
+ final PsiClass argClass = argResult.getElement();
+ if (argClass != null) {
+ if (wildcardParam.isExtends()) {
+ PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(boundClass, argClass, argResult.getSubstitutor());
+ if (superSubstitutor != null) {
+ for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(boundClass)) {
+ PsiType substituted = superSubstitutor.substitute(typeParameter);
+ if (substituted != null) {
+ Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(
+ boundResult.getSubstitutor().substitute(typeParameter), substituted, patternType, ConstraintType.EQUALS, depth + 1);
+ if (res != null) return res;
+ }
+ }
+ }
+ }
+ else {
+ PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(argClass, boundClass, boundResult.getSubstitutor());
+ if (superSubstitutor != null) {
+ for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(argClass)) {
+ PsiType substituted = argResult.getSubstitutor().substitute(typeParameter);
+ if (substituted != null) {
+ Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(
+ superSubstitutor.substitute(typeParameter), substituted, patternType, ConstraintType.EQUALS, depth + 1);
+ if (res != null) {
+ if (res == FAILED_INFERENCE) continue;
+ return res;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (param instanceof PsiArrayType && arg instanceof PsiArrayType) {
+ return getSubstitutionForTypeParameterInner(((PsiArrayType)param).getComponentType(), ((PsiArrayType)arg).getComponentType(),
+ patternType, constraintType, depth);
+ }
+
+ if (param instanceof PsiClassType && arg instanceof PsiClassType) {
+ PsiClassType.ClassResolveResult paramResult = ((PsiClassType)param).resolveGenerics();
+ PsiClass paramClass = paramResult.getElement();
+ if (paramClass == null) return null;
+
+ PsiClassType.ClassResolveResult argResult = ((PsiClassType)arg).resolveGenerics();
+ PsiClass argClass = argResult.getElement();
+ if (argClass != paramClass) {
+ return inferBySubtypingConstraint(patternType, constraintType, depth, paramClass, argClass);
+ }
+
+ PsiType lowerBound = PsiType.NULL;
+ PsiType upperBound = PsiType.NULL;
+ Pair<PsiType,ConstraintType> wildcardCaptured = null;
+ for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(paramClass)) {
+ PsiType paramType = paramResult.getSubstitutor().substitute(typeParameter);
+ PsiType argType = argResult.getSubstitutor().substituteWithBoundsPromotion(typeParameter);
+
+ if (wildcardCaptured != null) {
+ boolean alreadyFound = false;
+ for (PsiTypeParameter typeParam : PsiUtil.typeParametersIterable(paramClass)) {
+ if (typeParam != typeParameter &&
+ paramType != null &&
+ argResult.getSubstitutor().substituteWithBoundsPromotion(typeParam) == argType &&
+ paramType.equals(paramResult.getSubstitutor().substitute(typeParam))) {
+ alreadyFound = true;
+ }
+ }
+ if (alreadyFound) continue;
+ }
+
+ Pair<PsiType,ConstraintType> res = getSubstitutionForTypeParameterInner(paramType, argType, patternType, ConstraintType.EQUALS, depth + 1);
+
+ if (res != null) {
+ final PsiType type = res.getFirst();
+ switch (res.getSecond()) {
+ case EQUALS:
+ if (!(type instanceof PsiWildcardType)) return res;
+ if (wildcardCaptured != null) return FAILED_INFERENCE;
+ wildcardCaptured = res;
+ break;
+ case SUPERTYPE:
+ wildcardCaptured = res;
+ if (PsiType.NULL.equals(lowerBound)) {
+ lowerBound = type;
+ }
+ else if (!lowerBound.equals(type)) {
+ lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, type, myManager);
+ if (lowerBound == null) return FAILED_INFERENCE;
+ }
+ break;
+ case SUBTYPE:
+ wildcardCaptured = res;
+ if (PsiType.NULL.equals(upperBound) || TypeConversionUtil.isAssignable(upperBound, type)) {
+ upperBound = type;
+ }
+ }
+ }
+ }
+
+ if (lowerBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(lowerBound, ConstraintType.SUPERTYPE);
+ if (upperBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE);
+
+ return wildcardCaptured;
+ }
+
+ return null;
+ }
+
+ private static final Key<Boolean> inferSubtyping = Key.create("infer.subtyping.marker");
+ private Pair<PsiType, ConstraintType> inferBySubtypingConstraint(PsiType patternType,
+ ConstraintType constraintType,
+ int depth,
+ PsiClass paramClass,
+ PsiClass argClass) {
+ if (argClass instanceof PsiTypeParameter && paramClass instanceof PsiTypeParameter && PsiUtil.isLanguageLevel8OrHigher(argClass)) {
+ final Boolean alreadyInferBySubtyping = paramClass.getCopyableUserData(inferSubtyping);
+ if (alreadyInferBySubtyping != null) return null;
+ final PsiClassType[] argExtendsListTypes = argClass.getExtendsListTypes();
+ final PsiClassType[] paramExtendsListTypes = paramClass.getExtendsListTypes();
+ if (argExtendsListTypes.length == paramExtendsListTypes.length) {
+ try {
+ paramClass.putCopyableUserData(inferSubtyping, true);
+ for (int i = 0; i < argExtendsListTypes.length; i++) {
+ PsiClassType argBoundType = argExtendsListTypes[i];
+ PsiClassType paramBoundType = paramExtendsListTypes[i];
+ final PsiClassType.ClassResolveResult argResolveResult = argBoundType.resolveGenerics();
+ final PsiClassType.ClassResolveResult paramResolveResult = paramBoundType.resolveGenerics();
+ final PsiClass paramBoundClass = paramResolveResult.getElement();
+ final PsiClass argBoundClass = argResolveResult.getElement();
+ if (argBoundClass != null && paramBoundClass != null && paramBoundClass != argBoundClass) {
+ if (argBoundClass.isInheritor(paramBoundClass, true)) {
+ final PsiSubstitutor superClassSubstitutor =
+ TypeConversionUtil.getSuperClassSubstitutor(paramBoundClass, argBoundClass, argResolveResult.getSubstitutor());
+ argBoundType = JavaPsiFacade.getElementFactory(argClass.getProject()).createType(paramBoundClass, superClassSubstitutor);
+ } else {
+ return null;
+ }
+ }
+ final Pair<PsiType, ConstraintType> constraint =
+ getSubstitutionForTypeParameterInner(paramBoundType, argBoundType, patternType, constraintType, depth);
+ if (constraint != null) {
+ return constraint;
+ }
+ }
+ }
+ finally {
+ paramClass.putCopyableUserData(inferSubtyping, null);
+ }
+ }
+ }
+ return null;
+ }
+
+ private Pair<PsiType, ConstraintType> inferMethodTypeParameterFromParent(@NotNull final PsiElement parent,
+ @NotNull PsiExpression methodCall,
+ @NotNull PsiTypeParameter typeParameter,
+ @NotNull PsiSubstitutor substitutor,
+ @NotNull ParameterTypeInferencePolicy policy) {
+ Pair<PsiType, ConstraintType> constraint = null;
+ PsiType expectedType = PsiTypesUtil.getExpectedTypeByParent(methodCall);
+
+ if (expectedType == null) {
+ if (parent instanceof PsiReturnStatement) {
+ final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class);
+ if (lambdaExpression != null) {
+ return getFailedInferenceConstraint(typeParameter);
+ }
+ }
+ else if (parent instanceof PsiExpressionList) {
+ final PsiElement pParent = parent.getParent();
+ if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) {
+ constraint = policy.inferTypeConstraintFromCallContext(methodCall, (PsiExpressionList)parent, (PsiCallExpression)pParent, typeParameter);
+ if (constraint == null && PsiUtil.isLanguageLevel8OrHigher(methodCall)) {
+ constraint = graphInferenceFromCallContext(methodCall, typeParameter, (PsiCallExpression)pParent);
+ if (constraint != null) {
+ final PsiType constraintFirst = constraint.getFirst();
+ if (constraintFirst == null || constraintFirst.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
+ constraint = null;
+ }
+ }
+ }
+ }
+ } else if (parent instanceof PsiLambdaExpression) {
+ expectedType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(methodCall, true, new Computable<PsiType>() {
+ @Override
+ public PsiType compute() {
+ return LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
+ }
+ });
+ if (expectedType == null) {
+ return null;
+ }
+ expectedType = GenericsUtil.eliminateWildcards(expectedType);
+ } else if (parent instanceof PsiConditionalExpression) {
+ if (PsiUtil.isLanguageLevel8OrHigher(parent)) {
+ try {
+ final Pair<PsiType, ConstraintType> pair = inferFromConditionalExpression(parent, methodCall, typeParameter, substitutor, policy);
+ if (pair != null) {
+ return pair;
+ }
+ }
+ finally {
+ GraphInferencePolicy.forget(parent);
+ }
+ }
+ }
+ }
+
+ final GlobalSearchScope scope = parent.getResolveScope();
+ PsiType returnType = null;
+ if (constraint == null) {
+ if (expectedType == null) {
+ expectedType = methodCall instanceof PsiCallExpression ? policy.getDefaultExpectedType((PsiCallExpression)methodCall) : null;
+ }
+
+ returnType = ((PsiMethod)typeParameter.getOwner()).getReturnType();
+
+ constraint =
+ getSubstitutionForTypeParameterConstraint(typeParameter, returnType, expectedType, false, PsiUtil.getLanguageLevel(parent));
+
+ if (constraint != null) {
+ PsiType guess = constraint.getFirst();
+ if (guess != null &&
+ !guess.equals(PsiType.NULL) &&
+ constraint.getSecond() == ConstraintType.SUPERTYPE &&
+ guess instanceof PsiIntersectionType) {
+ for (PsiType conjuct : ((PsiIntersectionType)guess).getConjuncts()) {
+ if (!conjuct.isAssignableFrom(expectedType)) {
+ return FAILED_INFERENCE;
+ }
+ }
+ }
+ }
+ }
+
+ if (constraint == null) {
+ if (methodCall instanceof PsiCallExpression) {
+ final PsiExpressionList argumentList = ((PsiCallExpression)methodCall).getArgumentList();
+ if (argumentList != null && PsiUtil.getLanguageLevel(argumentList).isAtLeast(LanguageLevel.JDK_1_8)) {
+ for (PsiExpression expression : argumentList.getExpressions()) {
+ if (expression instanceof PsiLambdaExpression || expression instanceof PsiMethodReferenceExpression) {
+ final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType(expression, false);
+ if (functionalInterfaceType == null || PsiUtil.resolveClassInType(functionalInterfaceType) == typeParameter){
+ return getFailedInferenceConstraint(typeParameter);
+ }
+ final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
+
+ final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
+ if (method == null || methodParamsDependOn(typeParameter, expression,
+ functionalInterfaceType, method.getParameterList().getParameters(),
+ LambdaUtil.getSubstitutor(method, resolveResult))) {
+ if (expression instanceof PsiMethodReferenceExpression) {
+ return getFailedInferenceConstraint(typeParameter);
+ }
+ return null;
+ }
+ final Pair<PsiType, ConstraintType> inferredExceptionTypeConstraint = inferExceptionConstrains(typeParameter, expression, method, resolveResult.getSubstitutor());
+ if (inferredExceptionTypeConstraint != null) {
+ return inferredExceptionTypeConstraint;
+ }
+ }
+ }
+ }
+
+ PsiType[] superTypes = typeParameter.getSuperTypes();
+ if (superTypes.length == 0) return null;
+ final PsiType[] types = new PsiType[superTypes.length];
+ for (int i = 0; i < superTypes.length; i++) {
+ PsiType superType = substitutor.substitute(superTypes[i]);
+ if (superType instanceof PsiClassType && ((PsiClassType)superType).isRaw()) {
+ superType = TypeConversionUtil.erasure(superType);
+ }
+ if (superType == null) superType = PsiType.getJavaLangObject(myManager, scope);
+ if (superType == null) return null;
+ types[i] = superType;
+ }
+ return policy.getInferredTypeWithNoConstraint(myManager, PsiIntersectionType.createIntersection(types));
+ }
+ return null;
+ }
+ PsiType guess = constraint.getFirst();
+ guess = policy.adjustInferredType(myManager, guess, constraint.getSecond());
+
+ //The following code is the result of deep thought, do not shit it out before discussing with [ven]
+ if (returnType instanceof PsiClassType && typeParameter.equals(((PsiClassType)returnType).resolve())) {
+ PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
+ PsiSubstitutor newSubstitutor = substitutor.put(typeParameter, guess);
+ for (PsiClassType extendsType1 : extendsTypes) {
+ PsiType extendsType = newSubstitutor.substitute(extendsType1);
+ if (guess != null && !extendsType.isAssignableFrom(guess)) {
+ if (guess.isAssignableFrom(extendsType)) {
+ guess = extendsType;
+ newSubstitutor = substitutor.put(typeParameter, guess);
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
+
+ return new Pair<PsiType, ConstraintType>(guess, constraint.getSecond());
+ }
+
+ private static boolean checkSameExpression(PsiExpression templateExpr, final PsiExpression expression) {
+ return templateExpr.equals(PsiUtil.skipParenthesizedExprDown(expression));
+ }
+
+ private static Pair<PsiType, ConstraintType> inferExceptionConstrains(PsiTypeParameter typeParameter,
+ PsiExpression expression,
+ PsiMethod method,
+ PsiSubstitutor substitutor) {
+ final PsiClassType[] declaredExceptions = method.getThrowsList().getReferencedTypes();
+ for (PsiClassType exception : declaredExceptions) {
+ final PsiType substitute = substitutor.substitute(exception);
+ if (PsiUtil.resolveClassInType(substitute) == typeParameter) {
+ if (expression instanceof PsiLambdaExpression) {
+ final PsiElement body = ((PsiLambdaExpression)expression).getBody();
+ if (body != null) {
+ final List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(body);
+ if (unhandledExceptions.isEmpty()) {
+ return inferUncheckedException(typeParameter, exception, method);
+ }
+ }
+ }
+ else if (expression instanceof PsiMethodReferenceExpression) {
+ final PsiElement resolve = ((PsiMethodReferenceExpression)expression).resolve();
+ if (resolve instanceof PsiMethod) {
+ final PsiClassType[] declaredThrowsList = ((PsiMethod)resolve).getThrowsList().getReferencedTypes();
+ for (PsiClassType psiClassType : declaredThrowsList) {
+ if (!ExceptionUtil.isUncheckedException(psiClassType)) return null;
+ }
+ return inferUncheckedException(typeParameter, exception, method);
+ }
+ }
+ break;
+ }
+ }
+ return null;
+ }
+
+ private static Pair<PsiType, ConstraintType> inferUncheckedException(PsiTypeParameter typeParameter,
+ PsiClassType exception,
+ PsiMethod method) {
+ final Project project = typeParameter.getProject();
+ final PsiClass runtimeException = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION, method.getResolveScope());
+ if (runtimeException != null) {
+ for (PsiType superType : exception.getSuperTypes()) {
+ if (!InheritanceUtil.isInheritorOrSelf(runtimeException, PsiUtil.resolveClassInType(superType), true)) {
+ return getFailedInferenceConstraint(typeParameter);
+ }
+ }
+ return Pair.<PsiType, ConstraintType>create(JavaPsiFacade.getElementFactory(project).createType(runtimeException, PsiSubstitutor.EMPTY), ConstraintType.EQUALS);
+ }
+ return null;
+ }
+
+ private Pair<PsiType, ConstraintType> inferFromConditionalExpression(@NotNull PsiElement parent,
+ @NotNull PsiExpression methodCall,
+ @NotNull PsiTypeParameter typeParameter,
+ @NotNull PsiSubstitutor substitutor,
+ @NotNull ParameterTypeInferencePolicy policy) {
+ Pair<PsiType, ConstraintType> pair =
+ inferMethodTypeParameterFromParent(PsiUtil.skipParenthesizedExprUp(parent.getParent()), (PsiExpression)parent, typeParameter, substitutor, policy);
+ if (pair == null) {
+ final PsiExpression thenExpression = ((PsiConditionalExpression)parent).getThenExpression();
+ final PsiExpression elseExpression = ((PsiConditionalExpression)parent).getElseExpression();
+ final PsiType[] paramTypes = {((PsiMethod)typeParameter.getOwner()).getReturnType()};
+ if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(elseExpression)) && thenExpression != null) {
+ final PsiType thenType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(parent, true, new Computable<PsiType>() {
+ @Override
+ public PsiType compute() {
+ return thenExpression.getType();
+ }
+ });
+ if (thenType != null) {
+ pair = inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, new PsiType[] {thenType}, substitutor, null, policy);
+ }
+ } else if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(thenExpression)) && elseExpression != null) {
+ final PsiType elseType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(parent, true, new Computable<PsiType>() {
+ @Override
+ public PsiType compute() {
+ return elseExpression.getType();
+ }
+ });
+ if (elseType != null) {
+ pair = inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, new PsiType[] {elseType}, substitutor, null, policy);
+ }
+ }
+ }
+ return pair;
+ }
+
+ private static final ProcessCandidateParameterTypeInferencePolicy GRAPH_INFERENCE_POLICY = new GraphInferencePolicy();
+
+ private static Pair<PsiType, ConstraintType> graphInferenceFromCallContext(@NotNull final PsiExpression methodCall,
+ @NotNull final PsiTypeParameter typeParameter,
+ @NotNull final PsiCallExpression parentCall) {
+ if (Registry.is("disable.graph.inference", false)) return null;
+ final PsiExpressionList argumentList = parentCall.getArgumentList();
+ if (PsiDiamondType.ourDiamondGuard.currentStack().contains(parentCall)) {
+ PsiDiamondType.ourDiamondGuard.prohibitResultCaching(parentCall);
+ return FAILED_INFERENCE;
+ }
+ return PsiResolveHelper.ourGraphGuard.doPreventingRecursion(methodCall, true, new Computable<Pair<PsiType, ConstraintType>>() {
+ @Override
+ public Pair<PsiType, ConstraintType> compute() {
+ return GRAPH_INFERENCE_POLICY.inferTypeConstraintFromCallContext(methodCall, argumentList, parentCall, typeParameter);
+ }
+ });
+ }
+
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java
index 6410991c63c1..68bb8b865c92 100644
--- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java
@@ -15,38 +15,23 @@
*/
package com.intellij.psi.impl.source.resolve;
-import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.projectRoots.JavaSdkVersion;
-import com.intellij.openapi.projectRoots.JavaVersionService;
-import com.intellij.openapi.util.Computable;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.graphInference.PsiGraphInferenceHelper;
import com.intellij.psi.infos.CandidateInfo;
-import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.MethodProcessorSetupFailedException;
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
import com.intellij.psi.scope.processor.MethodResolverProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
-import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
-import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
-import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.List;
-import java.util.Map;
-
public class PsiResolveHelperImpl implements PsiResolveHelper {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl");
- public static final Pair<PsiType,ConstraintType> RAW_INFERENCE = new Pair<PsiType, ConstraintType>(null, ConstraintType.EQUALS);
private final PsiManager myManager;
public PsiResolveHelperImpl(PsiManager manager) {
@@ -155,314 +140,27 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
return processor.getCandidates();
}
- private Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(@NotNull PsiTypeParameter typeParameter,
- @NotNull PsiParameter[] parameters,
- @NotNull PsiExpression[] arguments,
- @NotNull PsiSubstitutor partialSubstitutor,
- final PsiElement parent,
- @NotNull ParameterTypeInferencePolicy policy) {
- PsiType[] paramTypes = new PsiType[arguments.length];
- PsiType[] argTypes = new PsiType[arguments.length];
- if (parameters.length > 0) {
- for (int j = 0; j < argTypes.length; j++) {
- final PsiExpression argument = arguments[j];
- if (argument == null) continue;
- if (argument instanceof PsiMethodCallExpression && ourGuard.currentStack().contains(argument)) continue;
-
- final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack();
- argTypes[j] = argument.getType();
- if (!stackStamp.mayCacheNow()) {
- argTypes[j] = null;
- continue;
- }
-
- final PsiParameter parameter = parameters[Math.min(j, parameters.length - 1)];
- if (j >= parameters.length && !parameter.isVarArgs()) break;
- paramTypes[j] = parameter.getType();
- if (paramTypes[j] instanceof PsiEllipsisType) {
- paramTypes[j] = ((PsiEllipsisType)paramTypes[j]).getComponentType();
- if (arguments.length == parameters.length &&
- argTypes[j] instanceof PsiArrayType &&
- !(((PsiArrayType)argTypes[j]).getComponentType() instanceof PsiPrimitiveType)) {
- argTypes[j] = ((PsiArrayType)argTypes[j]).getComponentType();
- }
- }
- }
- }
- return inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, argTypes, partialSubstitutor, parent, policy);
- }
-
- private Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(@NotNull PsiTypeParameter typeParameter,
- @NotNull PsiType[] paramTypes,
- @NotNull PsiType[] argTypes,
- @NotNull PsiSubstitutor partialSubstitutor,
- @Nullable PsiElement parent,
- @NotNull ParameterTypeInferencePolicy policy) {
- PsiWildcardType wildcardToCapture = null;
- Pair<PsiType, ConstraintType> rawInference = null;
- PsiType lowerBound = PsiType.NULL;
- PsiType upperBound = PsiType.NULL;
- if (paramTypes.length > 0) {
- sortLambdaExpressionsLast(paramTypes, argTypes);
- boolean rawType = false;
- boolean nullPassed = false;
- boolean lambdaRaw = false;
- for (int j = 0; j < argTypes.length; j++) {
- PsiType argumentType = argTypes[j];
- if (argumentType == null) continue;
- if (j >= paramTypes.length) break;
-
- PsiType parameterType = paramTypes[j];
- if (parameterType == null) break;
- rawType |= parameterType instanceof PsiClassType && ((PsiClassType)parameterType).isRaw();
- nullPassed |= argumentType == PsiType.NULL;
-
- if (parameterType instanceof PsiEllipsisType) {
- parameterType = ((PsiEllipsisType)parameterType).getComponentType();
- if (argTypes.length == paramTypes.length && argumentType instanceof PsiArrayType && !(((PsiArrayType)argumentType).getComponentType() instanceof PsiPrimitiveType)) {
- argumentType = ((PsiArrayType)argumentType).getComponentType();
- }
- }
- final Pair<PsiType,ConstraintType> currentSubstitution;
- if (argumentType instanceof PsiLambdaExpressionType) {
- currentSubstitution = inferSubstitutionFromLambda(typeParameter, (PsiLambdaExpressionType)argumentType, lowerBound, partialSubstitutor);
- if (rawType) {
- if (currentSubstitution == FAILED_INFERENCE || currentSubstitution == null && lowerBound == PsiType.NULL) return RAW_INFERENCE;
- }
- if (nullPassed && currentSubstitution == null) return RAW_INFERENCE;
- if (currentSubstitution != null && currentSubstitution.first == null) {
- lambdaRaw = true;
- }
- if (currentSubstitution == null && lambdaRaw) {
- return new Pair<PsiType, ConstraintType>(PsiType.getJavaLangObject(myManager, typeParameter.getResolveScope()), ConstraintType.EQUALS);
- }
- } else if (argumentType instanceof PsiMethodReferenceType) {
- final PsiMethodReferenceExpression referenceExpression = ((PsiMethodReferenceType)argumentType).getExpression();
- currentSubstitution = inferConstraintFromFunctionalInterfaceMethod(typeParameter, referenceExpression, partialSubstitutor.substitute(parameterType), partialSubstitutor, policy);
- }
- else {
- currentSubstitution = getSubstitutionForTypeParameterConstraint(typeParameter, parameterType,
- argumentType, true, PsiUtil.getLanguageLevel(typeParameter));
- }
- if (currentSubstitution == null) continue;
- if (currentSubstitution == FAILED_INFERENCE) {
- return getFailedInferenceConstraint(typeParameter);
- }
-
- final ConstraintType constraintType = currentSubstitution.getSecond();
- final PsiType type = currentSubstitution.getFirst();
- if (type == null) {
- rawInference = RAW_INFERENCE;
- continue;
- }
- switch(constraintType) {
- case EQUALS:
- if (!(type instanceof PsiWildcardType)) return currentSubstitution;
- if (wildcardToCapture != null) return getFailedInferenceConstraint(typeParameter);
- wildcardToCapture = (PsiWildcardType) type;
- break;
- case SUPERTYPE:
- if (PsiType.NULL.equals(lowerBound)) {
- lowerBound = type;
- }
- else if (!lowerBound.equals(type)) {
- lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, type, myManager);
- if (lowerBound == null) return getFailedInferenceConstraint(typeParameter);
- }
- break;
- case SUBTYPE:
- if (PsiType.NULL.equals(upperBound) || TypeConversionUtil.isAssignable(upperBound, type)) {
- upperBound = type;
- }
- }
- }
- }
-
- if (wildcardToCapture != null) {
- if (lowerBound != PsiType.NULL) {
- if (!wildcardToCapture.isAssignableFrom(lowerBound)) return getFailedInferenceConstraint(typeParameter);
- if (wildcardToCapture.isSuper()) {
- return new Pair<PsiType, ConstraintType>(wildcardToCapture, ConstraintType.SUPERTYPE);
- }
- lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, wildcardToCapture, myManager);
- }
- else {
- if (upperBound != PsiType.NULL && !upperBound.isAssignableFrom(wildcardToCapture)) return getFailedInferenceConstraint(typeParameter);
- return new Pair<PsiType, ConstraintType>(wildcardToCapture, ConstraintType.EQUALS);
- }
- }
-
- if (rawInference != null) return rawInference;
- if (lowerBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(lowerBound, ConstraintType.EQUALS);
-
- if (parent != null) {
- final Pair<PsiType, ConstraintType> constraint =
- inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy);
- if (constraint != null) {
- if (constraint.getSecond() != ConstraintType.SUBTYPE) {
- return constraint;
- }
-
- if (upperBound != PsiType.NULL) {
- return new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE);
- }
-
- return constraint;
- }
- }
-
- if (upperBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE);
- return null;
- }
-
- private static void sortLambdaExpressionsLast(@NotNull PsiType[] paramTypes, @NotNull PsiType[] argTypes) {
- for (int i = 0; i < argTypes.length; i++) {
- PsiType argType = argTypes[i];
- if ((argType instanceof PsiLambdaExpressionType || argType instanceof PsiMethodReferenceType) && i < argTypes.length - 1) {
- int k = i + 1;
- while((argTypes[k] instanceof PsiLambdaExpressionType || argTypes[k] instanceof PsiMethodReferenceType) && k < argTypes.length - 1) {
- k++;
- }
- if (!(argTypes[k] instanceof PsiLambdaExpressionType || argTypes[k] instanceof PsiMethodReferenceType)) {
- ArrayUtil.swap(paramTypes, i, k);
- ArrayUtil.swap(argTypes, i, k);
- i = k;
- }
- }
- }
- }
-
- private static Pair<PsiType, ConstraintType> getFailedInferenceConstraint(@NotNull PsiTypeParameter typeParameter) {
- return new Pair<PsiType, ConstraintType>(JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(typeParameter), ConstraintType.EQUALS);
- }
-
@Override
- public PsiType inferTypeForMethodTypeParameter(@NotNull final PsiTypeParameter typeParameter,
- @NotNull final PsiParameter[] parameters,
+ public PsiType inferTypeForMethodTypeParameter(@NotNull PsiTypeParameter typeParameter,
+ @NotNull PsiParameter[] parameters,
@NotNull PsiExpression[] arguments,
@NotNull PsiSubstitutor partialSubstitutor,
- PsiElement parent,
- @NotNull final ParameterTypeInferencePolicy policy) {
-
- final Pair<PsiType, ConstraintType> constraint =
- inferTypeForMethodTypeParameterInner(typeParameter, parameters, arguments, partialSubstitutor, parent, policy);
- if (constraint == null) return PsiType.NULL;
- return constraint.getFirst();
+ @Nullable PsiElement parent,
+ @NotNull ParameterTypeInferencePolicy policy) {
+ return getInferenceHelper(PsiUtil.getLanguageLevel(parent != null ? parent : typeParameter))
+ .inferTypeForMethodTypeParameter(typeParameter, parameters, arguments, partialSubstitutor, parent, policy);
}
- @NotNull
@Override
+ @NotNull
public PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters,
@NotNull PsiParameter[] parameters,
@NotNull PsiExpression[] arguments,
@NotNull PsiSubstitutor partialSubstitutor,
@NotNull PsiElement parent,
- @NotNull ParameterTypeInferencePolicy policy,
- @NotNull LanguageLevel languageLevel) {
- PsiType[] substitutions = new PsiType[typeParameters.length];
- @SuppressWarnings("unchecked")
- Pair<PsiType, ConstraintType>[] constraints = new Pair[typeParameters.length];
- for (int i = 0; i < typeParameters.length; i++) {
- if (substitutions[i] != null) continue;
- final Pair<PsiType, ConstraintType> constraint =
- inferTypeForMethodTypeParameterInner(typeParameters[i], parameters, arguments, partialSubstitutor, null, policy);
- constraints[i] = constraint;
- if (constraint != null && constraint.getSecond() != ConstraintType.SUBTYPE) {
- substitutions[i] = constraint.getFirst();
-
- if (substitutions[i] != null && languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { //try once more
- partialSubstitutor = partialSubstitutor.put(typeParameters[i], substitutions[i]);
- i = -1;
- }
- }
- }
-
- for (int i = 0; i < typeParameters.length; i++) {
- PsiTypeParameter typeParameter = typeParameters[i];
- if (substitutions[i] == null) {
- PsiType substitutionFromBounds = PsiType.NULL;
- OtherParameters:
- for (int j = 0; j < typeParameters.length; j++) {
- if (i != j) {
- PsiTypeParameter other = typeParameters[j];
- final PsiType otherSubstitution = substitutions[j];
- if (otherSubstitution == null) continue;
- final PsiClassType[] bounds = other.getExtendsListTypes();
- for (PsiClassType bound : bounds) {
- final PsiType substitutedBound = partialSubstitutor.substitute(bound);
- final Pair<PsiType, ConstraintType> currentConstraint =
- getSubstitutionForTypeParameterConstraint(typeParameter, substitutedBound, otherSubstitution, true, languageLevel);
- if (currentConstraint == null) continue;
- final PsiType currentSubstitution = currentConstraint.getFirst();
- final ConstraintType currentConstraintType = currentConstraint.getSecond();
- if (currentConstraintType == ConstraintType.EQUALS) {
- substitutionFromBounds = currentSubstitution;
- if (currentSubstitution == null) {
- constraints[i] = FAILED_INFERENCE;
- }
- break OtherParameters;
- }
- else if (currentConstraintType == ConstraintType.SUPERTYPE) {
- if (PsiType.NULL.equals(substitutionFromBounds)) {
- substitutionFromBounds = currentSubstitution;
- }
- else {
- substitutionFromBounds = GenericsUtil.getLeastUpperBound(substitutionFromBounds, currentSubstitution, myManager);
- }
- }
- }
-
- }
- }
-
- if (substitutionFromBounds != PsiType.NULL) substitutions[i] = substitutionFromBounds;
- }
- }
-
- for (int i = 0; i < typeParameters.length; i++) {
- PsiTypeParameter typeParameter = typeParameters[i];
- PsiType substitution = substitutions[i];
- if (substitution != PsiType.NULL) {
- partialSubstitutor = partialSubstitutor.put(typeParameter, substitution);
- }
- }
-
- try {
- for (int i = 0; i < typeParameters.length; i++) {
- PsiTypeParameter typeParameter = typeParameters[i];
- PsiType substitution = substitutions[i];
- if (substitution != null) continue;
-
- Pair<PsiType, ConstraintType> constraint = constraints[i];
- if (constraint == null) {
- constraint = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy);
- }
- else if (constraint.getSecond() == ConstraintType.SUBTYPE) {
- Pair<PsiType, ConstraintType> otherConstraint = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy);
- if (otherConstraint != null) {
- if (otherConstraint.getSecond() == ConstraintType.EQUALS || otherConstraint.getSecond() == ConstraintType.SUPERTYPE) {
- constraint = otherConstraint;
- }
- }
- }
-
- if (constraint != null) {
- substitution = constraint.getFirst();
- }
-
- if (substitution == null) {
- PsiElementFactory factory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
- return factory.createRawSubstitutor(partialSubstitutor, typeParameters);
- }
- if (substitution != PsiType.NULL) {
- partialSubstitutor = partialSubstitutor.put(typeParameter, substitution);
- }
- }
- }
- finally {
- GraphInferencePolicy.forget(parent);
- }
- return partialSubstitutor;
+ @NotNull ParameterTypeInferencePolicy policy) {
+ return getInferenceHelper(PsiUtil.getLanguageLevel(parent))
+ .inferTypeArguments(typeParameters, parameters, arguments, partialSubstitutor, parent, policy, PsiUtil.getLanguageLevel(parent));
}
@Override
@@ -472,8 +170,10 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
@NotNull PsiExpression[] arguments,
@NotNull PsiSubstitutor partialSubstitutor,
@NotNull PsiElement parent,
- @NotNull ParameterTypeInferencePolicy policy) {
- return inferTypeArguments(typeParameters, parameters, arguments, partialSubstitutor, parent, policy, PsiUtil.getLanguageLevel(parent));
+ @NotNull ParameterTypeInferencePolicy policy,
+ @NotNull LanguageLevel languageLevel) {
+ return getInferenceHelper(languageLevel)
+ .inferTypeArguments(typeParameters, parameters, arguments, partialSubstitutor, parent, policy, languageLevel);
}
@Override
@@ -482,866 +182,24 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
@NotNull PsiType[] leftTypes,
@NotNull PsiType[] rightTypes,
@NotNull LanguageLevel languageLevel) {
- if (leftTypes.length != rightTypes.length) throw new IllegalArgumentException("Types must be of the same length");
- PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
- for (PsiTypeParameter typeParameter : typeParameters) {
- PsiType substitution = PsiType.NULL;
- PsiType lowerBound = PsiType.NULL;
- for (int i1 = 0; i1 < leftTypes.length; i1++) {
- PsiType leftType = leftTypes[i1];
- PsiType rightType = rightTypes[i1];
- final Pair<PsiType, ConstraintType> constraint =
- getSubstitutionForTypeParameterConstraint(typeParameter, leftType, rightType, true, languageLevel);
- if (constraint != null) {
- final ConstraintType constraintType = constraint.getSecond();
- final PsiType current = constraint.getFirst();
- if (constraintType == ConstraintType.EQUALS) {
- substitution = current;
- break;
- }
- else if (constraintType == ConstraintType.SUBTYPE) {
- if (PsiType.NULL.equals(substitution)) {
- substitution = current;
- }
- else {
- substitution = GenericsUtil.getLeastUpperBound(substitution, current, myManager);
- }
- }
- else {
- if (PsiType.NULL.equals(lowerBound)) {
- lowerBound = current;
- }
- else {
- lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, current, myManager);
- }
- }
- }
- }
-
- if (PsiType.NULL.equals(substitution)) {
- substitution = lowerBound;
- }
-
- if (substitution != PsiType.NULL) {
- substitutor = substitutor.put(typeParameter, substitution);
- }
- }
- for (int i = 0; i < typeParameters.length; i++) {
- PsiTypeParameter typeParameter = typeParameters[i];
- if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
- PsiType substitutionFromBounds = PsiType.NULL;
- OtherParameters:
- for (int j = 0; j < typeParameters.length; j++) {
- if (i != j) {
- PsiTypeParameter other = typeParameters[j];
- final PsiType otherSubstitution = substitutor.substitute(other);
- if (otherSubstitution == null) continue;
- final PsiClassType[] bounds = other.getExtendsListTypes();
- for (PsiClassType bound : bounds) {
- final PsiType substitutedBound = substitutor.substitute(bound);
- final Pair<PsiType, ConstraintType> currentConstraint =
- getSubstitutionForTypeParameterConstraint(typeParameter, substitutedBound, otherSubstitution, true, languageLevel);
- if (currentConstraint == null) continue;
- final PsiType currentSubstitution = currentConstraint.getFirst();
- final ConstraintType currentConstraintType = currentConstraint.getSecond();
- if (currentConstraintType == ConstraintType.EQUALS) {
- substitutionFromBounds = currentSubstitution;
- break OtherParameters;
- }
- else if (currentConstraintType == ConstraintType.SUPERTYPE) {
- if (PsiType.NULL.equals(substitutionFromBounds)) {
- substitutionFromBounds = currentSubstitution;
- }
- else {
- substitutionFromBounds = GenericsUtil.getLeastUpperBound(substitutionFromBounds, currentSubstitution, myManager);
- }
- }
- }
- }
- }
- if (substitutionFromBounds != PsiType.NULL) {
- substitutor = substitutor.put(typeParameter, substitutionFromBounds);
- }
- }
- }
- return substitutor;
- }
-
- @Nullable
- private static Pair<PsiType, ConstraintType> processArgType(PsiType arg, final ConstraintType constraintType,
- final boolean captureWildcard) {
- if (arg instanceof PsiWildcardType && !captureWildcard) return FAILED_INFERENCE;
- if (arg != PsiType.NULL) {
- return new Pair<PsiType, ConstraintType>(arg, constraintType);
- }
- return null;
- }
-
- private Pair<PsiType, ConstraintType> inferMethodTypeParameterFromParent(@NotNull PsiTypeParameter typeParameter,
- @NotNull PsiSubstitutor substitutor,
- @NotNull PsiElement parent,
- @NotNull ParameterTypeInferencePolicy policy) {
- PsiTypeParameterListOwner owner = typeParameter.getOwner();
- Pair<PsiType, ConstraintType> substitution = null;
- if (owner instanceof PsiMethod && parent instanceof PsiCallExpression) {
- PsiCallExpression methodCall = (PsiCallExpression)parent;
- substitution = inferMethodTypeParameterFromParent(PsiUtil.skipParenthesizedExprUp(methodCall.getParent()), methodCall, typeParameter, substitutor, policy);
- }
- return substitution;
+ return getInferenceHelper(languageLevel)
+ .inferTypeArguments(typeParameters, leftTypes, rightTypes, languageLevel);
}
@Override
public PsiType getSubstitutionForTypeParameter(PsiTypeParameter typeParam,
PsiType param,
PsiType arg,
- boolean isContraVariantPosition,
- final LanguageLevel languageLevel) {
- final Pair<PsiType, ConstraintType> constraint = getSubstitutionForTypeParameterConstraint(typeParam, param, arg, isContraVariantPosition,
- languageLevel);
- return constraint == null ? PsiType.NULL : constraint.getFirst();
- }
-
- @Nullable
- public Pair<PsiType, ConstraintType> getSubstitutionForTypeParameterConstraint(PsiTypeParameter typeParam,
- PsiType param,
- PsiType arg,
- boolean isContraVariantPosition,
- final LanguageLevel languageLevel) {
- if (param instanceof PsiArrayType && arg instanceof PsiArrayType) {
- return getSubstitutionForTypeParameterConstraint(typeParam, ((PsiArrayType)param).getComponentType(), ((PsiArrayType)arg).getComponentType(),
- isContraVariantPosition, languageLevel);
- }
-
- if (!(param instanceof PsiClassType)) return null;
- PsiManager manager = myManager;
- if (arg instanceof PsiPrimitiveType) {
- if (!JavaVersionService.getInstance().isAtLeast(typeParam, JavaSdkVersion.JDK_1_7) && !isContraVariantPosition) return null;
- arg = ((PsiPrimitiveType)arg).getBoxedType(typeParam);
- if (arg == null) return null;
- }
-
- JavaResolveResult paramResult = ((PsiClassType)param).resolveGenerics();
- PsiClass paramClass = (PsiClass)paramResult.getElement();
- if (typeParam == paramClass) {
- final PsiClass psiClass = PsiUtil.resolveClassInType(arg);
- if (arg == null ||
- arg.getDeepComponentType() instanceof PsiPrimitiveType ||
- arg instanceof PsiIntersectionType ||
- (psiClass != null && (isContraVariantPosition || !CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName()) || (arg instanceof PsiArrayType)))) {
- PsiType bound = intersectAllExtends(typeParam, arg);
- return new Pair<PsiType, ConstraintType>(bound, ConstraintType.SUPERTYPE);
- }
- if (psiClass == null && arg instanceof PsiClassType) {
- return Pair.create(arg, ConstraintType.EQUALS);
- }
- return null;
- }
- if (paramClass == null) return null;
-
- if (!(arg instanceof PsiClassType)) return null;
-
- JavaResolveResult argResult = ((PsiClassType)arg).resolveGenerics();
- PsiClass argClass = (PsiClass)argResult.getElement();
- if (argClass == null) return null;
-
- PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
- PsiType patternType = factory.createType(typeParam);
- if (isContraVariantPosition) {
- PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(paramClass, argClass, argResult.getSubstitutor());
- if (substitutor == null) return null;
- arg = factory.createType(paramClass, substitutor, languageLevel);
- }
- else {
- PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(argClass, paramClass, paramResult.getSubstitutor());
- if (substitutor == null) return null;
- param = factory.createType(argClass, substitutor, languageLevel);
- }
-
- return getSubstitutionForTypeParameterInner(param, arg, patternType, ConstraintType.SUPERTYPE, 0);
- }
-
- @Nullable
- private Pair<PsiType, ConstraintType> inferSubstitutionFromLambda(PsiTypeParameter typeParam,
- PsiLambdaExpressionType arg,
- PsiType lowerBound,
- PsiSubstitutor partialSubstitutor) {
- final PsiLambdaExpression lambdaExpression = arg.getExpression();
- if (PsiUtil.getLanguageLevel(lambdaExpression).isAtLeast(LanguageLevel.JDK_1_8)) {
- final PsiElement parent = PsiUtil.skipParenthesizedExprUp(lambdaExpression.getParent());
- if (parent instanceof PsiExpressionList) {
- final PsiExpressionList expressionList = (PsiExpressionList)parent;
- final Map<PsiElement, Pair<PsiMethod, PsiSubstitutor>> methodMap = MethodCandidateInfo.CURRENT_CANDIDATE.get();
- final Pair<PsiMethod, PsiSubstitutor> pair = methodMap != null ? methodMap.get(expressionList) : null;
- if (pair != null) {
- final int i = LambdaUtil.getLambdaIdx(expressionList, lambdaExpression);
- if (i < 0) return null;
- final PsiParameter[] parameters = pair.first.getParameterList().getParameters();
- if (parameters.length <= i) return null;
- final PsiSubstitutor combinedSubst = pair.second.putAll(partialSubstitutor);
- methodMap.put(expressionList, Pair.create(pair.first, combinedSubst));
- return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression, combinedSubst.substitute(parameters[i].getType()), lowerBound);
- }
- }
- else {
- return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression,
- partialSubstitutor.substitute(lambdaExpression.getFunctionalInterfaceType()), lowerBound);
- }
- }
- return null;
+ boolean isContraVariantPosition,
+ LanguageLevel languageLevel) {
+ return getInferenceHelper(languageLevel)
+ .getSubstitutionForTypeParameter(typeParam, param, arg, isContraVariantPosition, languageLevel);
}
- @Nullable
- private Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(final PsiTypeParameter typeParam,
- final PsiMethodReferenceExpression methodReferenceExpression,
- final PsiType functionalInterfaceType,
- final PsiSubstitutor partialSubstitutor,
- final ParameterTypeInferencePolicy policy) {
- final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
- final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
- if (functionalInterfaceMethod != null) {
- final PsiSubstitutor subst = LambdaUtil.getSubstitutor(functionalInterfaceMethod, resolveResult);
- final PsiParameter[] methodParameters = functionalInterfaceMethod.getParameterList().getParameters();
- PsiType[] methodParamTypes = new PsiType[methodParameters.length];
- for (int i = 0; i < methodParameters.length; i++) {
- methodParamTypes[i] = GenericsUtil.eliminateWildcards(subst.substitute(methodParameters[i].getType()));
- }
-
- if (methodParamsDependOn(typeParam, methodReferenceExpression, functionalInterfaceType, methodParameters, subst)) {
- return null;
- }
-
- final PsiType[] args = new PsiType[methodParameters.length];
- Map<PsiMethodReferenceExpression,PsiType> map = PsiMethodReferenceUtil.ourRefs.get();
- if (map == null) {
- map = new HashMap<PsiMethodReferenceExpression, PsiType>();
- PsiMethodReferenceUtil.ourRefs.set(map);
- }
- final PsiType added = map.put(methodReferenceExpression, functionalInterfaceType);
- final JavaResolveResult methReferenceResolveResult;
- try {
- methReferenceResolveResult = methodReferenceExpression.advancedResolve(false);
- }
- finally {
- if (added == null) {
- map.remove(methodReferenceExpression);
- }
- }
- final PsiElement resolved = methReferenceResolveResult.getElement();
- if (resolved instanceof PsiMethod) {
- final PsiMethod method = (PsiMethod)resolved;
- final PsiParameter[] parameters = method.getParameterList().getParameters();
- boolean hasReceiver = false;
- if (methodParamTypes.length == parameters.length + 1) {
- if (!PsiMethodReferenceUtil
- .isReceiverType(methodParamTypes[0], method.getContainingClass(), methReferenceResolveResult.getSubstitutor())) return null;
- hasReceiver = true;
- } else if (parameters.length != methodParameters.length) {
- return null;
- }
- for (int i = 0; i < parameters.length; i++) {
- args[i] = methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(parameters[i].getType()));
- }
-
- final PsiType[] typesToInfer = hasReceiver ? ArrayUtil.remove(methodParamTypes, 0) : methodParamTypes;
- final Pair<PsiType, ConstraintType> constraint = inferTypeForMethodTypeParameterInner(typeParam, typesToInfer, args, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE);
- if (constraint != null){
- return constraint;
- }
- PsiType functionalInterfaceReturnType = functionalInterfaceMethod.getReturnType();
- if (functionalInterfaceReturnType != null && functionalInterfaceReturnType != PsiType.VOID) {
- functionalInterfaceReturnType = GenericsUtil.eliminateWildcards(subst.substitute(functionalInterfaceReturnType));
- final PsiType argType;
- if (method.isConstructor()) {
- argType = JavaPsiFacade.getElementFactory(functionalInterfaceMethod.getProject()).createType(method.getContainingClass(), methReferenceResolveResult.getSubstitutor());
- } else {
- argType = methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(method.getReturnType()));
- }
- final Pair<PsiType, ConstraintType> typeParameterConstraint =
- getSubstitutionForTypeParameterConstraint(typeParam, functionalInterfaceReturnType, argType, true, PsiUtil.getLanguageLevel(functionalInterfaceMethod));
- if (typeParameterConstraint != null && typeParameterConstraint.getSecond() != ConstraintType.EQUALS && method.isConstructor()) {
- final Pair<PsiType, ConstraintType> constraintFromParent =
- inferMethodTypeParameterFromParent(typeParam, partialSubstitutor, methodReferenceExpression.getParent().getParent(), policy);
- if (constraintFromParent != null && constraintFromParent.getSecond() == ConstraintType.EQUALS) return constraintFromParent;
- }
- return typeParameterConstraint;
- }
- }
- }
- return null;
- }
-
- @Nullable
- private Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(PsiTypeParameter typeParam,
- final PsiLambdaExpression lambdaExpression,
- final PsiType functionalInterfaceType,
- PsiType lowerBound) {
- final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
- final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
- if (method != null) {
- final PsiSubstitutor subst = LambdaUtil.getSubstitutor(method, resolveResult);
- final Pair<PsiType, ConstraintType> constraintFromFormalParams = inferConstraintFromLambdaFormalParams(typeParam, subst, method, lambdaExpression);
- if (constraintFromFormalParams != null) return constraintFromFormalParams;
-
- final PsiParameter[] methodParameters = method.getParameterList().getParameters();
- if (methodParamsDependOn(typeParam, lambdaExpression, functionalInterfaceType, methodParameters, subst)) {
- return null;
- }
-
- final PsiType returnType = subst.substitute(method.getReturnType());
- if (returnType != null && returnType != PsiType.VOID) {
- Pair<PsiType, ConstraintType> constraint = null;
- final List<PsiExpression> expressions = LambdaUtil.getReturnExpressions(lambdaExpression);
- for (final PsiExpression expression : expressions) {
- final boolean independent = lambdaExpression.hasFormalParameterTypes() || LambdaUtil.isFreeFromTypeInferenceArgs(methodParameters, lambdaExpression, expression, subst, functionalInterfaceType, typeParam);
- if (!independent) {
- if (lowerBound != PsiType.NULL) {
- return null;
- }
- continue;
- }
- if (expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).resolve() == null) continue;
- PsiType exprType = ourGraphGuard.doPreventingRecursion(expression, true, new Computable<PsiType>() {
- @Override
- public PsiType compute() {
- return expression.getType();
- }
- });
- if (exprType instanceof PsiLambdaParameterType) {
- final PsiParameter parameter = ((PsiLambdaParameterType)exprType).getParameter();
- final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter);
- if (parameterIndex > -1) {
- exprType = subst.substitute(methodParameters[parameterIndex].getType());
- }
- } else if (exprType instanceof PsiLambdaExpressionType) {
- return inferConstraintFromFunctionalInterfaceMethod(typeParam, ((PsiLambdaExpressionType)exprType).getExpression(), returnType,
- lowerBound);
- } else if (exprType == null && independent) {
- return null;
- }
-
- if (exprType == null){
- return FAILED_INFERENCE;
- }
-
- final Pair<PsiType, ConstraintType> returnExprConstraint =
- getSubstitutionForTypeParameterConstraint(typeParam, GenericsUtil.eliminateWildcards(returnType), exprType, true, PsiUtil.getLanguageLevel(method));
- if (returnExprConstraint != null) {
- if (returnExprConstraint == FAILED_INFERENCE) return returnExprConstraint;
- if (constraint != null) {
- final PsiType leastUpperBound = GenericsUtil.getLeastUpperBound(constraint.getFirst(), returnExprConstraint.getFirst(), myManager);
- constraint = new Pair<PsiType, ConstraintType>(leastUpperBound, ConstraintType.SUPERTYPE);
- } else {
- constraint = returnExprConstraint;
- }
- }
- }
- if (constraint != null) return constraint;
- }
- }
- return null;
- }
-
- private static boolean methodParamsDependOn(PsiTypeParameter typeParam, PsiElement psiElement,
- PsiType functionalInterfaceType,
- PsiParameter[] methodParameters,
- PsiSubstitutor subst) {
- for (PsiParameter parameter : methodParameters) {
- if (LambdaUtil.dependsOnTypeParams(subst.substitute(parameter.getType()), functionalInterfaceType, psiElement, typeParam)) {
- return true;
- }
- }
- return false;
- }
-
- @Nullable
- private Pair<PsiType, ConstraintType> inferConstraintFromLambdaFormalParams(PsiTypeParameter typeParam,
- PsiSubstitutor subst,
- PsiMethod method, PsiLambdaExpression lambdaExpression) {
- final PsiParameter[] parameters = lambdaExpression.getParameterList().getParameters();
- if (parameters.length == 0) return null;
- final PsiType[] lambdaArgs = new PsiType[parameters.length];
- for (int i = 0; i < parameters.length; i++) {
- PsiParameter parameter = parameters[i];
- if (parameter.getTypeElement() == null) {
- return null;
- }
- lambdaArgs[i] = parameter.getType();
- }
-
- final PsiParameter[] methodParameters = method.getParameterList().getParameters();
- PsiType[] methodParamTypes = new PsiType[methodParameters.length];
- for (int i = 0; i < methodParameters.length; i++) {
- methodParamTypes[i] = GenericsUtil.eliminateWildcards(subst.substitute(methodParameters[i].getType()));
- }
- return inferTypeForMethodTypeParameterInner(typeParam, methodParamTypes, lambdaArgs, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE);
- }
-
- private static PsiType intersectAllExtends(PsiTypeParameter typeParam, PsiType arg) {
- if (arg == null) return null;
- PsiClassType[] superTypes = typeParam.getSuperTypes();
- PsiType[] erasureTypes = new PsiType[superTypes.length];
- for (int i = 0; i < superTypes.length; i++) {
- erasureTypes[i] = TypeConversionUtil.erasure(superTypes[i]);
- }
- PsiType[] types = ArrayUtil.append(erasureTypes, arg, PsiType.class);
- assert types.length != 0;
- return PsiIntersectionType.createIntersection(types);
- }
-
- //represents the result of failed type inference: in case we failed inferring from parameters, do not perform inference from context
- private static final Pair<PsiType, ConstraintType> FAILED_INFERENCE = new Pair<PsiType, ConstraintType>(PsiType.NULL, ConstraintType.EQUALS);
-
- @Nullable
- private Pair<PsiType, ConstraintType> getSubstitutionForTypeParameterInner(PsiType param,
- PsiType arg,
- PsiType patternType,
- final ConstraintType constraintType,
- final int depth) {
- if (arg instanceof PsiCapturedWildcardType && (depth < 2 || constraintType != ConstraintType.EQUALS)) arg = ((PsiCapturedWildcardType)arg).getWildcard(); //reopen
-
- if (patternType.equals(param)) {
- return processArgType(arg, constraintType, depth < 2);
- }
-
- if (param instanceof PsiWildcardType) {
- final PsiWildcardType wildcardParam = (PsiWildcardType)param;
- final PsiType paramBound = wildcardParam.getBound();
- if (paramBound == null) return null;
- ConstraintType constrType = wildcardParam.isExtends() ? ConstraintType.SUPERTYPE : ConstraintType.SUBTYPE;
- if (arg instanceof PsiWildcardType) {
- if (((PsiWildcardType)arg).isExtends() == wildcardParam.isExtends() && ((PsiWildcardType)arg).isBounded() == wildcardParam.isBounded()) {
- Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(paramBound, ((PsiWildcardType)arg).getBound(),
- patternType, constrType, depth);
- if (res != null) return res;
- }
- }
- else if (patternType.equals(paramBound)) {
- Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(paramBound, arg,
- patternType, constrType, depth);
- if (res != null) return res;
- }
- else if (paramBound instanceof PsiArrayType && arg instanceof PsiArrayType) {
- Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(((PsiArrayType) paramBound).getComponentType(),
- ((PsiArrayType) arg).getComponentType(),
- patternType, constrType, depth);
- if (res != null) return res;
- }
- else if (paramBound instanceof PsiClassType && arg instanceof PsiClassType) {
- final PsiClassType.ClassResolveResult boundResult = ((PsiClassType)paramBound).resolveGenerics();
- final PsiClass boundClass = boundResult.getElement();
- if (boundClass != null) {
- final PsiClassType.ClassResolveResult argResult = ((PsiClassType)arg).resolveGenerics();
- final PsiClass argClass = argResult.getElement();
- if (argClass != null) {
- if (wildcardParam.isExtends()) {
- PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(boundClass, argClass, argResult.getSubstitutor());
- if (superSubstitutor != null) {
- for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(boundClass)) {
- PsiType substituted = superSubstitutor.substitute(typeParameter);
- if (substituted != null) {
- Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(
- boundResult.getSubstitutor().substitute(typeParameter), substituted, patternType, ConstraintType.EQUALS, depth + 1);
- if (res != null) return res;
- }
- }
- }
- }
- else {
- PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(argClass, boundClass, boundResult.getSubstitutor());
- if (superSubstitutor != null) {
- for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(argClass)) {
- PsiType substituted = argResult.getSubstitutor().substitute(typeParameter);
- if (substituted != null) {
- Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(
- superSubstitutor.substitute(typeParameter), substituted, patternType, ConstraintType.EQUALS, depth + 1);
- if (res != null) {
- if (res == FAILED_INFERENCE) continue;
- return res;
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- if (param instanceof PsiArrayType && arg instanceof PsiArrayType) {
- return getSubstitutionForTypeParameterInner(((PsiArrayType)param).getComponentType(), ((PsiArrayType)arg).getComponentType(),
- patternType, constraintType, depth);
- }
-
- if (param instanceof PsiClassType && arg instanceof PsiClassType) {
- PsiClassType.ClassResolveResult paramResult = ((PsiClassType)param).resolveGenerics();
- PsiClass paramClass = paramResult.getElement();
- if (paramClass == null) return null;
-
- PsiClassType.ClassResolveResult argResult = ((PsiClassType)arg).resolveGenerics();
- PsiClass argClass = argResult.getElement();
- if (argClass != paramClass) {
- return inferBySubtypingConstraint(patternType, constraintType, depth, paramClass, argClass);
- }
-
- PsiType lowerBound = PsiType.NULL;
- PsiType upperBound = PsiType.NULL;
- Pair<PsiType,ConstraintType> wildcardCaptured = null;
- for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(paramClass)) {
- PsiType paramType = paramResult.getSubstitutor().substitute(typeParameter);
- PsiType argType = argResult.getSubstitutor().substituteWithBoundsPromotion(typeParameter);
-
- if (wildcardCaptured != null) {
- boolean alreadyFound = false;
- for (PsiTypeParameter typeParam : PsiUtil.typeParametersIterable(paramClass)) {
- if (typeParam != typeParameter &&
- paramType != null &&
- argResult.getSubstitutor().substituteWithBoundsPromotion(typeParam) == argType &&
- paramType.equals(paramResult.getSubstitutor().substitute(typeParam))) {
- alreadyFound = true;
- }
- }
- if (alreadyFound) continue;
- }
-
- Pair<PsiType,ConstraintType> res = getSubstitutionForTypeParameterInner(paramType, argType, patternType, ConstraintType.EQUALS, depth + 1);
-
- if (res != null) {
- final PsiType type = res.getFirst();
- switch (res.getSecond()) {
- case EQUALS:
- if (!(type instanceof PsiWildcardType)) return res;
- if (wildcardCaptured != null) return FAILED_INFERENCE;
- wildcardCaptured = res;
- break;
- case SUPERTYPE:
- wildcardCaptured = res;
- if (PsiType.NULL.equals(lowerBound)) {
- lowerBound = type;
- }
- else if (!lowerBound.equals(type)) {
- lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, type, myManager);
- if (lowerBound == null) return FAILED_INFERENCE;
- }
- break;
- case SUBTYPE:
- wildcardCaptured = res;
- if (PsiType.NULL.equals(upperBound) || TypeConversionUtil.isAssignable(upperBound, type)) {
- upperBound = type;
- }
- }
- }
- }
-
- if (lowerBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(lowerBound, ConstraintType.SUPERTYPE);
- if (upperBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE);
-
- return wildcardCaptured;
- }
-
- return null;
- }
-
- private static final Key<Boolean> inferSubtyping = Key.create("infer.subtyping.marker");
- private Pair<PsiType, ConstraintType> inferBySubtypingConstraint(PsiType patternType,
- ConstraintType constraintType,
- int depth,
- PsiClass paramClass,
- PsiClass argClass) {
- if (argClass instanceof PsiTypeParameter && paramClass instanceof PsiTypeParameter && PsiUtil.isLanguageLevel8OrHigher(argClass)) {
- final Boolean alreadyInferBySubtyping = paramClass.getCopyableUserData(inferSubtyping);
- if (alreadyInferBySubtyping != null) return null;
- final PsiClassType[] argExtendsListTypes = argClass.getExtendsListTypes();
- final PsiClassType[] paramExtendsListTypes = paramClass.getExtendsListTypes();
- if (argExtendsListTypes.length == paramExtendsListTypes.length) {
- try {
- paramClass.putCopyableUserData(inferSubtyping, true);
- for (int i = 0; i < argExtendsListTypes.length; i++) {
- PsiClassType argBoundType = argExtendsListTypes[i];
- PsiClassType paramBoundType = paramExtendsListTypes[i];
- final PsiClassType.ClassResolveResult argResolveResult = argBoundType.resolveGenerics();
- final PsiClassType.ClassResolveResult paramResolveResult = paramBoundType.resolveGenerics();
- final PsiClass paramBoundClass = paramResolveResult.getElement();
- final PsiClass argBoundClass = argResolveResult.getElement();
- if (argBoundClass != null && paramBoundClass != null && paramBoundClass != argBoundClass) {
- if (argBoundClass.isInheritor(paramBoundClass, true)) {
- final PsiSubstitutor superClassSubstitutor =
- TypeConversionUtil.getSuperClassSubstitutor(paramBoundClass, argBoundClass, argResolveResult.getSubstitutor());
- argBoundType = JavaPsiFacade.getElementFactory(argClass.getProject()).createType(paramBoundClass, superClassSubstitutor);
- } else {
- return null;
- }
- }
- final Pair<PsiType, ConstraintType> constraint =
- getSubstitutionForTypeParameterInner(paramBoundType, argBoundType, patternType, constraintType, depth);
- if (constraint != null) {
- return constraint;
- }
- }
- }
- finally {
- paramClass.putCopyableUserData(inferSubtyping, null);
- }
- }
- }
- return null;
- }
-
- private Pair<PsiType, ConstraintType> inferMethodTypeParameterFromParent(@NotNull final PsiElement parent,
- @NotNull PsiExpression methodCall,
- @NotNull PsiTypeParameter typeParameter,
- @NotNull PsiSubstitutor substitutor,
- @NotNull ParameterTypeInferencePolicy policy) {
- Pair<PsiType, ConstraintType> constraint = null;
- PsiType expectedType = PsiTypesUtil.getExpectedTypeByParent(methodCall);
-
- if (expectedType == null) {
- if (parent instanceof PsiReturnStatement) {
- final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class);
- if (lambdaExpression != null) {
- return getFailedInferenceConstraint(typeParameter);
- }
- }
- else if (parent instanceof PsiExpressionList) {
- final PsiElement pParent = parent.getParent();
- if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) {
- constraint = policy.inferTypeConstraintFromCallContext(methodCall, (PsiExpressionList)parent, (PsiCallExpression)pParent, typeParameter);
- if (constraint == null && PsiUtil.isLanguageLevel8OrHigher(methodCall)) {
- constraint = graphInferenceFromCallContext(methodCall, typeParameter, (PsiCallExpression)pParent);
- if (constraint != null) {
- final PsiType constraintFirst = constraint.getFirst();
- if (constraintFirst == null || constraintFirst.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
- constraint = null;
- }
- }
- }
- }
- } else if (parent instanceof PsiLambdaExpression) {
- expectedType = ourGraphGuard.doPreventingRecursion(methodCall, true, new Computable<PsiType>() {
- @Override
- public PsiType compute() {
- return LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
- }
- });
- if (expectedType == null) {
- return null;
- }
- expectedType = GenericsUtil.eliminateWildcards(expectedType);
- } else if (parent instanceof PsiConditionalExpression) {
- if (PsiUtil.isLanguageLevel8OrHigher(parent)) {
- try {
- final Pair<PsiType, ConstraintType> pair = inferFromConditionalExpression(parent, methodCall, typeParameter, substitutor, policy);
- if (pair != null) {
- return pair;
- }
- }
- finally {
- GraphInferencePolicy.forget(parent);
- }
- }
- }
- }
-
- final GlobalSearchScope scope = parent.getResolveScope();
- PsiType returnType = null;
- if (constraint == null) {
- if (expectedType == null) {
- expectedType = methodCall instanceof PsiCallExpression ? policy.getDefaultExpectedType((PsiCallExpression)methodCall) : null;
- }
-
- returnType = ((PsiMethod)typeParameter.getOwner()).getReturnType();
-
- constraint =
- getSubstitutionForTypeParameterConstraint(typeParameter, returnType, expectedType, false, PsiUtil.getLanguageLevel(parent));
-
- if (constraint != null) {
- PsiType guess = constraint.getFirst();
- if (guess != null &&
- !guess.equals(PsiType.NULL) &&
- constraint.getSecond() == ConstraintType.SUPERTYPE &&
- guess instanceof PsiIntersectionType) {
- for (PsiType conjuct : ((PsiIntersectionType)guess).getConjuncts()) {
- if (!conjuct.isAssignableFrom(expectedType)) {
- return FAILED_INFERENCE;
- }
- }
- }
- }
- }
-
- if (constraint == null) {
- if (methodCall instanceof PsiCallExpression) {
- final PsiExpressionList argumentList = ((PsiCallExpression)methodCall).getArgumentList();
- if (argumentList != null && PsiUtil.getLanguageLevel(argumentList).isAtLeast(LanguageLevel.JDK_1_8)) {
- for (PsiExpression expression : argumentList.getExpressions()) {
- if (expression instanceof PsiLambdaExpression || expression instanceof PsiMethodReferenceExpression) {
- final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType(expression, false);
- if (functionalInterfaceType == null || PsiUtil.resolveClassInType(functionalInterfaceType) == typeParameter){
- return getFailedInferenceConstraint(typeParameter);
- }
- final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
-
- final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
- if (method == null || methodParamsDependOn(typeParameter, expression,
- functionalInterfaceType, method.getParameterList().getParameters(),
- LambdaUtil.getSubstitutor(method, resolveResult))) {
- if (expression instanceof PsiMethodReferenceExpression) {
- return getFailedInferenceConstraint(typeParameter);
- }
- return null;
- }
- final Pair<PsiType, ConstraintType> inferredExceptionTypeConstraint = inferExceptionConstrains(typeParameter, expression, method, resolveResult.getSubstitutor());
- if (inferredExceptionTypeConstraint != null) {
- return inferredExceptionTypeConstraint;
- }
- }
- }
- }
-
- PsiType[] superTypes = typeParameter.getSuperTypes();
- if (superTypes.length == 0) return null;
- final PsiType[] types = new PsiType[superTypes.length];
- for (int i = 0; i < superTypes.length; i++) {
- PsiType superType = substitutor.substitute(superTypes[i]);
- if (superType instanceof PsiClassType && ((PsiClassType)superType).isRaw()) {
- superType = TypeConversionUtil.erasure(superType);
- }
- if (superType == null) superType = PsiType.getJavaLangObject(myManager, scope);
- if (superType == null) return null;
- types[i] = superType;
- }
- return policy.getInferredTypeWithNoConstraint(myManager, PsiIntersectionType.createIntersection(types));
- }
- return null;
- }
- PsiType guess = constraint.getFirst();
- guess = policy.adjustInferredType(myManager, guess, constraint.getSecond());
-
- //The following code is the result of deep thought, do not shit it out before discussing with [ven]
- if (returnType instanceof PsiClassType && typeParameter.equals(((PsiClassType)returnType).resolve())) {
- PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
- PsiSubstitutor newSubstitutor = substitutor.put(typeParameter, guess);
- for (PsiClassType extendsType1 : extendsTypes) {
- PsiType extendsType = newSubstitutor.substitute(extendsType1);
- if (guess != null && !extendsType.isAssignableFrom(guess)) {
- if (guess.isAssignableFrom(extendsType)) {
- guess = extendsType;
- newSubstitutor = substitutor.put(typeParameter, guess);
- }
- else {
- break;
- }
- }
- }
- }
-
- return new Pair<PsiType, ConstraintType>(guess, constraint.getSecond());
- }
-
- private static boolean checkSameExpression(PsiExpression templateExpr, final PsiExpression expression) {
- return templateExpr.equals(PsiUtil.skipParenthesizedExprDown(expression));
- }
-
- private static Pair<PsiType, ConstraintType> inferExceptionConstrains(PsiTypeParameter typeParameter,
- PsiExpression expression,
- PsiMethod method,
- PsiSubstitutor substitutor) {
- final PsiClassType[] declaredExceptions = method.getThrowsList().getReferencedTypes();
- for (PsiClassType exception : declaredExceptions) {
- final PsiType substitute = substitutor.substitute(exception);
- if (PsiUtil.resolveClassInType(substitute) == typeParameter) {
- if (expression instanceof PsiLambdaExpression) {
- final PsiElement body = ((PsiLambdaExpression)expression).getBody();
- if (body != null) {
- final List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(body);
- if (unhandledExceptions.isEmpty()) {
- return inferUncheckedException(typeParameter, exception, method);
- }
- }
- }
- else if (expression instanceof PsiMethodReferenceExpression) {
- final PsiElement resolve = ((PsiMethodReferenceExpression)expression).resolve();
- if (resolve instanceof PsiMethod) {
- final PsiClassType[] declaredThrowsList = ((PsiMethod)resolve).getThrowsList().getReferencedTypes();
- for (PsiClassType psiClassType : declaredThrowsList) {
- if (!ExceptionUtil.isUncheckedException(psiClassType)) return null;
- }
- return inferUncheckedException(typeParameter, exception, method);
- }
- }
- break;
- }
- }
- return null;
- }
-
- private static Pair<PsiType, ConstraintType> inferUncheckedException(PsiTypeParameter typeParameter,
- PsiClassType exception,
- PsiMethod method) {
- final Project project = typeParameter.getProject();
- final PsiClass runtimeException = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION, method.getResolveScope());
- if (runtimeException != null) {
- for (PsiType superType : exception.getSuperTypes()) {
- if (!InheritanceUtil.isInheritorOrSelf(runtimeException, PsiUtil.resolveClassInType(superType), true)) {
- return getFailedInferenceConstraint(typeParameter);
- }
- }
- return Pair.<PsiType, ConstraintType>create(JavaPsiFacade.getElementFactory(project).createType(runtimeException, PsiSubstitutor.EMPTY), ConstraintType.EQUALS);
- }
- return null;
- }
-
- private Pair<PsiType, ConstraintType> inferFromConditionalExpression(@NotNull PsiElement parent,
- @NotNull PsiExpression methodCall,
- @NotNull PsiTypeParameter typeParameter,
- @NotNull PsiSubstitutor substitutor,
- @NotNull ParameterTypeInferencePolicy policy) {
- Pair<PsiType, ConstraintType> pair =
- inferMethodTypeParameterFromParent(PsiUtil.skipParenthesizedExprUp(parent.getParent()), (PsiExpression)parent, typeParameter, substitutor, policy);
- if (pair == null) {
- final PsiExpression thenExpression = ((PsiConditionalExpression)parent).getThenExpression();
- final PsiExpression elseExpression = ((PsiConditionalExpression)parent).getElseExpression();
- final PsiType[] paramTypes = {((PsiMethod)typeParameter.getOwner()).getReturnType()};
- if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(elseExpression)) && thenExpression != null) {
- final PsiType thenType = ourGraphGuard.doPreventingRecursion(parent, true, new Computable<PsiType>() {
- @Override
- public PsiType compute() {
- return thenExpression.getType();
- }
- });
- if (thenType != null) {
- pair = inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, new PsiType[] {thenType}, substitutor, null, policy);
- }
- } else if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(thenExpression)) && elseExpression != null) {
- final PsiType elseType = ourGraphGuard.doPreventingRecursion(parent, true, new Computable<PsiType>() {
- @Override
- public PsiType compute() {
- return elseExpression.getType();
- }
- });
- if (elseType != null) {
- pair = inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, new PsiType[] {elseType}, substitutor, null, policy);
- }
- }
- }
- return pair;
- }
-
- private static final ProcessCandidateParameterTypeInferencePolicy GRAPH_INFERENCE_POLICY = new GraphInferencePolicy();
-
- private static Pair<PsiType, ConstraintType> graphInferenceFromCallContext(@NotNull final PsiExpression methodCall,
- @NotNull final PsiTypeParameter typeParameter,
- @NotNull final PsiCallExpression parentCall) {
- if (Registry.is("disable.graph.inference", false)) return null;
- final PsiExpressionList argumentList = parentCall.getArgumentList();
- if (PsiDiamondType.ourDiamondGuard.currentStack().contains(parentCall)) {
- PsiDiamondType.ourDiamondGuard.prohibitResultCaching(parentCall);
- return FAILED_INFERENCE;
+ public PsiInferenceHelper getInferenceHelper(LanguageLevel languageLevel) {
+ if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && Registry.is("enable.graph.inference", false)) {
+ return new PsiGraphInferenceHelper(myManager);
}
- return ourGraphGuard.doPreventingRecursion(methodCall, true, new Computable<Pair<PsiType, ConstraintType>>() {
- @Override
- public Pair<PsiType, ConstraintType> compute() {
- return GRAPH_INFERENCE_POLICY.inferTypeConstraintFromCallContext(methodCall, argumentList, parentCall, typeParameter);
- }
- });
+ return new PsiOldInferenceHelper(myManager);
}
}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceBound.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceBound.java
new file mode 100644
index 000000000000..df63a7cfec88
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceBound.java
@@ -0,0 +1,25 @@
+/*
+ * 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.psi.impl.source.resolve.graphInference;
+
+/**
+* User: anna
+*/
+public enum InferenceBound {
+ UPPER,
+ LOWER,
+ EQ
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java
new file mode 100644
index 000000000000..a63e43824986
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java
@@ -0,0 +1,187 @@
+/*
+ * 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.psi.impl.source.resolve.graphInference;
+
+import com.intellij.psi.PsiType;
+import com.intellij.psi.impl.source.resolve.graphInference.constraints.ConstraintFormula;
+import com.intellij.psi.impl.source.resolve.graphInference.constraints.SubtypingConstraint;
+import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeEqualityConstraint;
+
+import java.util.List;
+
+/**
+ * User: anna
+ */
+public class InferenceIncorporationPhase {
+ private InferenceSession mySession;
+
+ public InferenceIncorporationPhase(InferenceSession session) {
+ mySession = session;
+ }
+
+ public void incorporate() {
+ for (InferenceVariable inferenceVariable : mySession.getInferenceVariables()) {
+ if (inferenceVariable.getInstantiation() != PsiType.NULL) continue;
+ final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ);
+ final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER);
+ final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER);
+ /*
+ todo inference errors
+ Infer infer = inferenceContext.infer();
+ for (Type e : uv.getBounds(InferenceBound.EQ)) {
+ if (e.containsAny(inferenceContext.inferenceVars())) continue;
+ for (Type u : uv.getBounds(InferenceBound.UPPER)) {
+ if (!isSubtype(e, inferenceContext.asFree(u), warn, infer)) {
+ infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER);
+ }
+ }
+ for (Type l : uv.getBounds(InferenceBound.LOWER)) {
+ if (!isSubtype(inferenceContext.asFree(l), e, warn, infer)) {
+ infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER);
+ }
+ }
+ }
+ */
+
+ eqEq(eqBounds);
+ upperLower(upperBounds, lowerBounds);
+
+ upDown(eqBounds, upperBounds);
+ upDown(lowerBounds, eqBounds);
+ }
+ }
+
+ boolean isFullyIncorporated() {
+ boolean needFurtherIncorporation = false;
+ for (InferenceVariable inferenceVariable : mySession.getInferenceVariables()) {
+ if (inferenceVariable.getInstantiation() != PsiType.NULL) continue;
+ final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ);
+ final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER);
+ final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER);
+ needFurtherIncorporation |= crossVariables(inferenceVariable, upperBounds, lowerBounds, InferenceBound.LOWER);
+ needFurtherIncorporation |= crossVariables(inferenceVariable, lowerBounds, upperBounds, InferenceBound.UPPER);
+
+ needFurtherIncorporation |= eqCrossVariables(inferenceVariable, eqBounds);
+ }
+ return !needFurtherIncorporation;
+ }
+
+ /**
+ * a = b imply every bound of a matches a bound of b and vice versa
+ */
+ private boolean eqCrossVariables(InferenceVariable inferenceVariable, List<PsiType> eqBounds) {
+ boolean needFurtherIncorporation = false;
+ for (PsiType eqBound : eqBounds) {
+ final InferenceVariable inferenceVar = mySession.getInferenceVariable(eqBound);
+ if (inferenceVar != null) {
+ if (inferenceVar.isCaptured()) continue;
+ //inferenceVar.addBound(inferenceVariable.qType, InferenceVariable.InferenceBound.EQ);
+ for (InferenceBound inferenceBound : InferenceBound.values()) {
+ for (PsiType bound : inferenceVariable.getBounds(inferenceBound)) {
+ if (mySession.getInferenceVariable(bound) != inferenceVar) {
+ needFurtherIncorporation |= inferenceVar.addBound(bound, inferenceBound);
+ }
+ }
+ for (PsiType bound : inferenceVar.getBounds(inferenceBound)) {
+ if (mySession.getInferenceVariable(bound) != inferenceVariable) {
+ needFurtherIncorporation |= inferenceVariable.addBound(bound, inferenceBound);
+ }
+ }
+ }
+ }
+ }
+ return needFurtherIncorporation;
+ }
+
+ /**
+ * a < b & S <: a & b <: T imply S <: b & a <: T
+ */
+ private boolean crossVariables(InferenceVariable inferenceVariable,
+ List<PsiType> upperBounds,
+ List<PsiType> lowerBounds,
+ InferenceBound inferenceBound) {
+
+ final InferenceBound oppositeBound = inferenceBound == InferenceBound.LOWER
+ ? InferenceBound.UPPER
+ : InferenceBound.LOWER;
+ boolean result = false;
+ for (PsiType upperBound : upperBounds) {
+ final InferenceVariable inferenceVar = mySession.getInferenceVariable(upperBound);
+ if (inferenceVar != null) {
+ if (inferenceVar.isCaptured()) continue;
+ //todo inferenceVar.addBound(inferenceVariable.qType, inferenceBound);
+ for (PsiType lowerBound : lowerBounds) {
+ result |= inferenceVar.addBound(lowerBound, inferenceBound);
+ }
+
+
+ for (PsiType varUpperBound : inferenceVar.getBounds(oppositeBound)) {
+ result |= inferenceVariable.addBound(varUpperBound, oppositeBound);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * a = S & a <: T imply S <: T
+ * or
+ * a = S & T <: a imply T <: S
+ */
+ private void upDown(List<PsiType> eqBounds, List<PsiType> upperBounds) {
+ for (PsiType upperBound : upperBounds) {
+ final boolean properType = mySession.isProperType(upperBound);
+ for (PsiType eqBound : eqBounds) {
+ if (properType && mySession.isProperType(eqBound)) continue;
+ if (!upperBound.equals(eqBound)) {
+ addConstraint(new SubtypingConstraint(upperBound, eqBound, true));
+ }
+ }
+ }
+ }
+
+ /**
+ * S <: a & a <: T imply S <: T
+ */
+ private void upperLower(List<PsiType> upperBounds, List<PsiType> lowerBounds) {
+ for (PsiType upperBound : upperBounds) {
+ final boolean properType = mySession.isProperType(upperBound);
+ for (PsiType lowerBound : lowerBounds) {
+ if (properType && mySession.isProperType(lowerBound)) continue;
+ if (!upperBound.equals(lowerBound)) {
+ addConstraint(new SubtypingConstraint(upperBound, lowerBound, true));
+ }
+ }
+ }
+ }
+
+ /**
+ * a = S & a = T imply S = T
+ */
+ private void eqEq(List<PsiType> eqBounds) {
+ for (int i = 0; i < eqBounds.size(); i++) {
+ PsiType sBound= eqBounds.get(i);
+ for (int j = i + 1; j < eqBounds.size(); j++) {
+ final PsiType tBound = eqBounds.get(j);
+ addConstraint(new TypeEqualityConstraint(tBound, sBound));
+ }
+ }
+ }
+
+ private void addConstraint(ConstraintFormula constraint) {
+ mySession.addConstraint(constraint);
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java
new file mode 100644
index 000000000000..dba4a7289a5e
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java
@@ -0,0 +1,333 @@
+/*
+ * 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.psi.impl.source.resolve.graphInference;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.graphInference.constraints.ConstraintFormula;
+import com.intellij.psi.impl.source.resolve.graphInference.constraints.ExpressionCompatibilityConstraint;
+import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeCompatibilityConstraint;
+import com.intellij.psi.infos.MethodCandidateInfo;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.psi.util.TypeConversionUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * User: anna
+ */
+public class InferenceSession {
+ private static final Logger LOG = Logger.getInstance("#" + InferenceSession.class.getName());
+
+ private Map<PsiTypeParameter, InferenceVariable> myInferenceVariables = new LinkedHashMap<PsiTypeParameter, InferenceVariable>();
+ private final List<ConstraintFormula> myConstraints = new ArrayList<ConstraintFormula>();
+ private final List<ConstraintFormula> myDelayedConstraints = new ArrayList<ConstraintFormula>();
+
+ private PsiSubstitutor mySiteSubstitutor;
+ private PsiManager myManager;
+ private int myConstraintIdx = 0;
+
+ private final InferenceIncorporationPhase myIncorporationPhase = new InferenceIncorporationPhase(this);
+
+ public InferenceSession(PsiTypeParameter[] typeParams,
+ PsiType[] leftTypes,
+ PsiType[] rightTypes,
+ PsiSubstitutor siteSubstitutor,
+ PsiManager manager) {
+ myManager = manager;
+ mySiteSubstitutor = siteSubstitutor;
+
+ initBounds(typeParams);
+
+ LOG.assertTrue(leftTypes.length == rightTypes.length);
+ for (int i = 0; i < leftTypes.length; i++) {
+ myConstraints.add(new TypeCompatibilityConstraint(leftTypes[i], mySiteSubstitutor.substitute(rightTypes[i])));
+ }
+ }
+
+ public InferenceSession(PsiTypeParameter[] typeParams,
+ PsiParameter[] parameters,
+ PsiExpression[] args,
+ PsiSubstitutor siteSubstitutor,
+ PsiElement parent,
+ PsiManager manager) {
+ myManager = manager;
+ mySiteSubstitutor = siteSubstitutor;
+
+ initBounds(typeParams);
+
+ if (parameters.length > 0) {
+ for (int i = 0; i < args.length; i++) {
+ PsiType parameterType = mySiteSubstitutor.substitute(parameters[i < parameters.length ? i : parameters.length - 1].getType());
+ if (parameterType instanceof PsiEllipsisType) {
+ if (args.length != parameters.length || args[i] != null && !(args[i].getType() instanceof PsiArrayType)) {
+ parameterType = ((PsiEllipsisType)parameterType).getComponentType();
+ }
+ }
+ if (args[i] != null) {
+ myConstraints.add(new ExpressionCompatibilityConstraint(args[i], parameterType));
+ }
+ }
+ }
+
+ if (parent instanceof PsiCallExpression) {
+ final Map<PsiElement, Pair<PsiMethod, PsiSubstitutor>> map = MethodCandidateInfo.CURRENT_CANDIDATE.get();
+ if (map != null) {
+ final Pair<PsiMethod, PsiSubstitutor> pair = map.get(((PsiCallExpression)parent).getArgumentList());
+ if (pair != null) {
+ initReturnTypeConstraint(pair.first, (PsiCallExpression)parent);
+ }
+ }
+ }
+ }
+
+ @NotNull
+ public PsiSubstitutor infer() {
+ repeatInferencePhases();
+
+ for (InferenceVariable inferenceVariable : myInferenceVariables.values()) {
+ final PsiTypeParameter typeParameter = inferenceVariable.getParameter();
+ PsiType instantiation = inferenceVariable.getInstantiation();
+ if (instantiation == PsiType.NULL) {
+ //failed inference
+ mySiteSubstitutor = mySiteSubstitutor
+ .put(typeParameter, JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(typeParameter));
+ }
+ }
+ return mySiteSubstitutor;
+ }
+
+ private void initBounds(PsiTypeParameter[] typeParameters) {
+ for (PsiTypeParameter parameter : typeParameters) {
+ myInferenceVariables.put(parameter, new InferenceVariable(parameter));
+ }
+
+ for (InferenceVariable variable : myInferenceVariables.values()) {
+ final PsiTypeParameter parameter = variable.getParameter();
+ boolean added = false;
+ final PsiClassType[] extendsListTypes = parameter.getExtendsListTypes();
+ for (PsiType classType : extendsListTypes) {
+ classType = mySiteSubstitutor.substitute(classType);
+ if (isProperType(classType)) {
+ added = true;
+ }
+ variable.addBound(classType, InferenceBound.UPPER);
+ }
+ if (!added) {
+ variable.addBound(PsiType.getJavaLangObject(parameter.getManager(), parameter.getResolveScope()),
+ InferenceBound.UPPER);
+ }
+ }
+ }
+
+ private void initReturnTypeConstraint(PsiMethod method, PsiCallExpression context) {
+ if (PsiPolyExpressionUtil.isPolyExpression(context) ||
+ context instanceof PsiNewExpression && PsiDiamondType.ourDiamondGuard.currentStack().contains(context)) {
+ final PsiType returnType = method.getReturnType();
+ if (!PsiType.VOID.equals(returnType) && returnType != null) {
+ final PsiType targetType = PsiPolyExpressionUtil.getTargetType(context);//todo primitive type
+ if (targetType != null) {
+ myConstraints.add(new TypeCompatibilityConstraint(targetType, returnType));
+ }
+ }
+ }
+ }
+
+ public InferenceVariable getInferenceVariable(PsiType psiType) {
+ final PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(psiType);
+ if (psiClass instanceof PsiTypeParameter) {
+ final InferenceVariable inferenceVariable = myInferenceVariables.get(psiClass);
+ if (inferenceVariable != null) {
+ return inferenceVariable;
+ }
+ }
+ return null;
+ }
+
+ public boolean isProperType(@Nullable PsiType type) {
+ return collectDependencies(type, null);
+ }
+
+ public boolean collectDependencies(@Nullable PsiType type, @Nullable final Set<InferenceVariable> dependencies) {
+ if (type == null) return true;
+ final Boolean isProper = type.accept(new PsiTypeVisitor<Boolean>() {
+ @Nullable
+ @Override
+ public Boolean visitType(PsiType type) {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Boolean visitArrayType(PsiArrayType arrayType) {
+ return arrayType.getComponentType().accept(this);
+ }
+
+ @Nullable
+ @Override
+ public Boolean visitWildcardType(PsiWildcardType wildcardType) {
+ final PsiType bound = wildcardType.getBound();
+ if (bound == null) return true;
+ return bound.accept(this);
+ }
+
+ @Nullable
+ @Override
+ public Boolean visitClassType(PsiClassType classType) {
+ final InferenceVariable inferenceVariable = getInferenceVariable(classType);
+ if (inferenceVariable != null) {
+ if (dependencies != null) {
+ dependencies.add(inferenceVariable);
+ return true;
+ }
+ return false;
+ }
+ for (PsiType psiType : classType.getParameters()) {
+ if (!psiType.accept(this)) return false;
+ }
+ return true;
+ }
+ });
+ return dependencies != null ? !dependencies.isEmpty() : isProper;
+ }
+
+ private void repeatInferencePhases() {
+ do {
+ if (!reduceConstraints()) {
+ //inference error occurred
+ return;
+ }
+ myIncorporationPhase.incorporate();
+
+ } while (!myIncorporationPhase.isFullyIncorporated() || myConstraintIdx < myConstraints.size());
+
+ resolveBounds();
+ }
+
+ private boolean reduceConstraints() {
+ List<ConstraintFormula> newConstraints = new ArrayList<ConstraintFormula>();
+ for (int i = myConstraintIdx; i < myConstraints.size(); i++) {
+ ConstraintFormula constraint = myConstraints.get(i);
+ if (!constraint.reduce(this, newConstraints, myDelayedConstraints)) {
+ return false;
+ }
+ }
+ myConstraintIdx = myConstraints.size();
+ for (ConstraintFormula constraint : newConstraints) {
+ addConstraint(constraint);
+ }
+ return true;
+ }
+
+ private void resolveBounds() {
+ final List<List<InferenceVariable>> independentVars = InferenceVariablesOrder.resolveOrder(myInferenceVariables.values(), this);
+ for (List<InferenceVariable> variables : independentVars) {
+ for (InferenceVariable inferenceVariable : variables) {
+
+ if (inferenceVariable.getInstantiation() != PsiType.NULL) continue;
+ final PsiTypeParameter typeParameter = inferenceVariable.getParameter();
+ try {
+ final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ);
+ final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER);
+ final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER);
+ if (/*eqBounds.contains(null) || lowerBounds.contains(null) || */upperBounds.contains(null)) {
+ inferenceVariable.setInstantiation(null);
+ continue;
+ }
+ PsiType bound = null;
+ for (PsiType eqBound : eqBounds) {
+ eqBound = acceptBoundsWithRecursiveDependencies(typeParameter, eqBound);
+ if (isProperType(eqBound)) {
+ bound = eqBound;
+ break;
+ }
+ }
+ if (bound != null) {
+ inferenceVariable.setInstantiation(bound);
+ } else {
+ PsiType lub = null;
+ for (PsiType lowerBound : lowerBounds) {
+ lowerBound = acceptBoundsWithRecursiveDependencies(typeParameter, lowerBound);
+ if (isProperType(lowerBound)) {
+ if (lub == null) {
+ lub = lowerBound;
+ }
+ else {
+ lub = GenericsUtil.getLeastUpperBound(lub, lowerBound, myManager);
+ }
+ }
+ }
+ if (lub != null) {
+ inferenceVariable.setInstantiation(lub instanceof PsiCapturedWildcardType ? ((PsiCapturedWildcardType)lub).getWildcard() : lub);
+ }
+ else {
+ PsiType glb = null;
+ for (PsiType upperBound : upperBounds) {
+ upperBound = acceptBoundsWithRecursiveDependencies(typeParameter, upperBound);
+ if (isProperType(upperBound)) {
+ if (glb == null) {
+ glb = upperBound;
+ }
+ else {
+ glb = GenericsUtil.getGreatestLowerBound(glb, upperBound);
+ }
+ }
+ }
+ if (glb != null) {
+ inferenceVariable.setInstantiation(glb);
+ }
+ }
+ }
+ }
+ finally {
+ final PsiType instantiation = inferenceVariable.getInstantiation();
+ if (instantiation != PsiType.NULL) {
+ mySiteSubstitutor = mySiteSubstitutor.put(typeParameter, instantiation);
+ }
+ }
+ }
+ }
+ }
+
+ private PsiType acceptBoundsWithRecursiveDependencies(PsiTypeParameter typeParameter, PsiType bound) {
+ if (PsiPolyExpressionUtil.mentionsTypeParameters(bound, Collections.singleton(typeParameter))) {
+ return mySiteSubstitutor.put(typeParameter, null).substitute(bound);
+ }
+ return bound;
+ }
+
+ public PsiManager getManager() {
+ return myManager;
+ }
+
+ public GlobalSearchScope getScope() {
+ return GlobalSearchScope.allScope(myManager.getProject());
+ }
+
+ public Collection<InferenceVariable> getInferenceVariables() {
+ return myInferenceVariables.values();
+ }
+
+ public void addConstraint(ConstraintFormula constraint) {
+ if (!myConstraints.contains(constraint)) {
+ myConstraints.add(constraint);
+ }
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java
new file mode 100644
index 000000000000..e69f229fe91f
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.psi.impl.source.resolve.graphInference;
+
+import com.intellij.psi.*;
+
+import java.util.*;
+
+/**
+ * User: anna
+ */
+public class InferenceVariable {
+ private boolean myCaptured;
+
+ public PsiTypeParameter getParameter() {
+ return myParameter;
+ }
+
+ private Map<InferenceBound, List<PsiType>> myBounds = new HashMap<InferenceBound, List<PsiType>>();
+ private PsiTypeParameter myParameter;
+
+ private PsiType myInstantiation = PsiType.NULL;
+ public InferenceVariable(PsiTypeParameter parameter) {
+ myParameter = parameter;
+ }
+ public PsiType getInstantiation() {
+ return myInstantiation;
+ }
+
+ public void setInstantiation(PsiType instantiation) {
+ myInstantiation = instantiation;
+ }
+
+ public boolean isCaptured() {
+ return myCaptured;
+ }
+
+ public void setCaptured(boolean captured) {
+ myCaptured = captured;
+ }
+
+ public boolean addBound(PsiType classType, InferenceBound inferenceBound) {
+ List<PsiType> list = myBounds.get(inferenceBound);
+ if (list == null) {
+ list = new ArrayList<PsiType>();
+ myBounds.put(inferenceBound, list);
+ }
+ if (!list.contains(classType)) {
+ list.add(classType);
+ return true;
+ }
+ return false;
+ }
+
+ public List<PsiType> getBounds(InferenceBound inferenceBound) {
+ final List<PsiType> bounds = myBounds.get(inferenceBound);
+ return bounds != null ? new ArrayList<PsiType>(bounds) : Collections.<PsiType>emptyList();
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariablesOrder.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariablesOrder.java
new file mode 100644
index 000000000000..4a2c4126611e
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariablesOrder.java
@@ -0,0 +1,184 @@
+/*
+ * 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.psi.impl.source.resolve.graphInference;
+
+import com.intellij.psi.PsiType;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+
+import java.util.*;
+
+/**
+ * User: anna
+ * Date: 7/4/13
+ */
+
+public class InferenceVariablesOrder {
+
+ public static List<List<InferenceVariable>> resolveOrder(Collection<InferenceVariable> vars, InferenceSession session) {
+ Map<InferenceVariable, InferenceGraphNode<InferenceVariable>> nodes =
+ new HashMap<InferenceVariable, InferenceGraphNode<InferenceVariable>>();
+ for (InferenceVariable var : vars) {
+ nodes.put(var, new InferenceGraphNode<InferenceVariable>(var));
+ }
+
+ for (InferenceVariable var : vars) {
+ if (var.getInstantiation() != PsiType.NULL) continue;
+ final InferenceGraphNode<InferenceVariable> node = nodes.get(var);
+ for (InferenceBound inferenceBound : InferenceBound.values()) {
+ for (PsiType bound : var.getBounds(inferenceBound)) {
+ final HashSet<InferenceVariable> dependencies = new HashSet<InferenceVariable>();
+ session.collectDependencies(bound, dependencies);
+ for (InferenceVariable dependentVariable : dependencies) {
+ final InferenceGraphNode<InferenceVariable> dependency = nodes.get(dependentVariable);
+ if (dependency != null) {
+ node.addDependency(dependency);
+ }
+ }
+ }
+ }
+ }
+ final ArrayList<InferenceGraphNode<InferenceVariable>> acyclicNodes = initNodes(nodes.values());
+ return ContainerUtil.map(acyclicNodes, new Function<InferenceGraphNode<InferenceVariable>, List<InferenceVariable>>() {
+ @Override
+ public List<InferenceVariable> fun(InferenceGraphNode<InferenceVariable> node) {
+ return node.getValue();
+ }
+ });
+ }
+
+ public static <T> List<List<InferenceGraphNode<T>>> tarjan(Collection<InferenceGraphNode<T>> nodes) {
+ final ArrayList<List<InferenceGraphNode<T>>> result = new ArrayList<List<InferenceGraphNode<T>>>();
+ final Stack<InferenceGraphNode<T>> currentStack = new Stack<InferenceGraphNode<T>>();
+ int index = 0;
+ for (InferenceGraphNode<T> node : nodes) {
+ if (node.index == -1) {
+ index += InferenceGraphNode.strongConnect(node, index, currentStack, result);
+ }
+ }
+ return result;
+ }
+
+ public static <T> ArrayList<InferenceGraphNode<T>> initNodes(Collection<InferenceGraphNode<T>> allNodes) {
+ final List<List<InferenceGraphNode<T>>> nodes = tarjan(allNodes);
+ final ArrayList<InferenceGraphNode<T>> acyclicNodes = new ArrayList<InferenceGraphNode<T>>();
+ for (List<InferenceGraphNode<T>> cycle : nodes) {
+ acyclicNodes.add(InferenceGraphNode.merge(cycle, allNodes));
+ }
+ return acyclicNodes;
+ }
+
+ public static class InferenceGraphNode<T> {
+ private final List<T> myValue = new ArrayList<T>();
+ private Set<InferenceGraphNode<T>> myDependencies = new HashSet<InferenceGraphNode<T>>();
+
+ private int index = -1;
+ private int lowlink;
+
+ public InferenceGraphNode(T value) {
+ myValue.add(value);
+ }
+
+ public List<T> getValue() {
+ return myValue;
+ }
+
+ public Set<InferenceGraphNode<T>> getDependencies() {
+ return myDependencies;
+ }
+
+ public void addDependency(InferenceGraphNode<T> node) {
+ myDependencies.add(node);
+ }
+
+
+ private static <T> InferenceGraphNode<T> merge(final List<InferenceGraphNode<T>> cycle,
+ final Collection<InferenceGraphNode<T>> allNodes) {
+ assert !cycle.isEmpty();
+ final InferenceGraphNode<T> root = cycle.get(0);
+ if (cycle.size() > 1) {
+ for (int i = 1; i < cycle.size(); i++) {
+ final InferenceGraphNode<T> cycleNode = cycle.get(i);
+
+ root.copyFrom(cycleNode);
+ root.filterInterCycleDependencies();
+
+ for (InferenceGraphNode<T> node : allNodes) {
+ if (node.myDependencies.remove(cycleNode)) {
+ node.myDependencies.add(root);
+ }
+ }
+ }
+ }
+ return root;
+ }
+
+ private void filterInterCycleDependencies() {
+ boolean includeSelfDependency = false;
+ for (Iterator<InferenceGraphNode<T>> iterator = myDependencies.iterator(); iterator.hasNext(); ) {
+ InferenceGraphNode<T> d = iterator.next();
+ assert d.myValue.size() >= 1;
+ final T initialNodeValue = d.myValue.get(0);
+ if (myValue.contains(initialNodeValue)) {
+ includeSelfDependency = true;
+ iterator.remove();
+ }
+ }
+
+ if (includeSelfDependency) {
+ myDependencies.add(this);
+ }
+ }
+
+ private void copyFrom(final InferenceGraphNode<T> cycleNode) {
+ myValue.addAll(cycleNode.myValue);
+ myDependencies.addAll(cycleNode.myDependencies);
+ }
+
+ private static <T> int strongConnect(InferenceGraphNode<T> currentNode,
+ int index,
+ Stack<InferenceGraphNode<T>> currentStack,
+ ArrayList<List<InferenceGraphNode<T>>> result) {
+ currentNode.index = index;
+ currentNode.lowlink = index;
+ index++;
+
+ currentStack.push(currentNode);
+
+ for (InferenceGraphNode<T> dependantNode : currentNode.getDependencies()) {
+ if (dependantNode.index == -1) {
+ strongConnect(dependantNode, index, currentStack, result);
+ currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.lowlink);
+ }
+ else if (currentStack.contains(dependantNode)) {
+ currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.index);
+ }
+ }
+
+ if (currentNode.lowlink == currentNode.index) {
+ final ArrayList<InferenceGraphNode<T>> arrayList = new ArrayList<InferenceGraphNode<T>>();
+ InferenceGraphNode<T> cyclicNode;
+ do {
+ cyclicNode = currentStack.pop();
+ arrayList.add(cyclicNode);
+ }
+ while (cyclicNode != currentNode);
+ result.add(arrayList);
+ }
+ return index;
+ }
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiGraphInferenceHelper.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiGraphInferenceHelper.java
new file mode 100644
index 000000000000..372f67b7e8e3
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiGraphInferenceHelper.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.psi.impl.source.resolve.graphInference;
+
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
+import com.intellij.psi.impl.source.resolve.PsiOldInferenceHelper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * User: anna
+ */
+public class PsiGraphInferenceHelper implements PsiInferenceHelper {
+ private final PsiManager myManager;
+
+ public PsiGraphInferenceHelper(PsiManager manager) {
+ myManager = manager;
+ }
+
+ @Override
+ public PsiType inferTypeForMethodTypeParameter(@NotNull PsiTypeParameter typeParameter,
+ @NotNull PsiParameter[] parameters,
+ @NotNull PsiExpression[] arguments,
+ @NotNull PsiSubstitutor partialSubstitutor,
+ @Nullable PsiElement parent,
+ @NotNull ParameterTypeInferencePolicy policy) {
+ final InferenceSession inferenceSession =
+ new InferenceSession(new PsiTypeParameter[]{typeParameter}, parameters, arguments, partialSubstitutor, parent, myManager);
+ return inferenceSession.infer().substitute(typeParameter);
+ }
+
+ @NotNull
+ @Override
+ public PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters,
+ @NotNull PsiParameter[] parameters,
+ @NotNull PsiExpression[] arguments,
+ @NotNull PsiSubstitutor partialSubstitutor,
+ @NotNull PsiElement parent,
+ @NotNull ParameterTypeInferencePolicy policy,
+ @NotNull LanguageLevel languageLevel) {
+ if (typeParameters.length == 0) return partialSubstitutor;
+ return new InferenceSession(typeParameters, parameters, arguments, partialSubstitutor, parent, myManager).infer();
+ }
+
+ @NotNull
+ @Override
+ public PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters,
+ @NotNull PsiType[] leftTypes,
+ @NotNull PsiType[] rightTypes,
+ @NotNull LanguageLevel languageLevel) {
+ if (typeParameters.length == 0) return PsiSubstitutor.EMPTY;
+ return new InferenceSession(typeParameters, leftTypes, rightTypes, PsiSubstitutor.EMPTY, myManager).infer();
+ }
+
+ @Override
+ public PsiType getSubstitutionForTypeParameter(PsiTypeParameter typeParam,
+ PsiType param,
+ PsiType arg,
+ boolean isContraVariantPosition,
+ LanguageLevel languageLevel) {
+ return new PsiOldInferenceHelper(myManager).getSubstitutionForTypeParameter(typeParam, param, arg, isContraVariantPosition, languageLevel);
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java
index f3611e939bc4..2e425e0fb436 100644
--- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java
@@ -16,7 +16,10 @@
package com.intellij.psi.impl.source.resolve.graphInference;
import com.intellij.psi.*;
+import com.intellij.psi.util.PsiTypesUtil;
+import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
@@ -52,19 +55,19 @@ public class PsiPolyExpressionUtil {
if (parameterList != null) {
final PsiTypeElement[] typeElements = parameterList.getTypeParameterElements();
if (typeElements.length == 1 && typeElements[0].getType() instanceof PsiDiamondType) {
- return isAssignmentOrInvocationContext(expression.getParent());
+ return isInAssignmentOrInvocationContext(expression);
}
}
}
} else if (expression instanceof PsiMethodCallExpression) {
- if (isAssignmentOrInvocationContext(expression.getParent()) && ((PsiMethodCallExpression)expression).getTypeArguments().length == 0) {
+ if (isInAssignmentOrInvocationContext(expression) && ((PsiMethodCallExpression)expression).getTypeArguments().length == 0) {
final PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod();
if (method != null) {
final Set<PsiTypeParameter> typeParameters = new HashSet<PsiTypeParameter>(Arrays.asList(method.getTypeParameters()));
if (typeParameters.size() > 0) {
final PsiType returnType = method.getReturnType();
if (returnType != null) {
- return returnTypeMentionsTypeParameters(typeParameters, returnType);
+ return mentionsTypeParameters(returnType, typeParameters);
}
}
}
@@ -73,13 +76,18 @@ public class PsiPolyExpressionUtil {
else if (expression instanceof PsiConditionalExpression) {
final ConditionalKind conditionalKind = isBooleanOrNumeric(expression);
if (conditionalKind == null) {
- return isAssignmentOrInvocationContext(expression.getParent());
+ return isInAssignmentOrInvocationContext(expression);
}
}
return false;
}
- private static Boolean returnTypeMentionsTypeParameters(final Set<PsiTypeParameter> typeParameters, PsiType returnType) {
+ public static PsiType getTargetType(@NotNull PsiCallExpression expression) {
+ return PsiTypesUtil.getExpectedTypeByParent(expression);
+ }
+
+ public static Boolean mentionsTypeParameters(@Nullable PsiType returnType, final Set<PsiTypeParameter> typeParameters) {
+ if (returnType == null) return false;
return returnType.accept(new PsiTypeVisitor<Boolean>() {
@Nullable
@Override
@@ -89,6 +97,16 @@ public class PsiPolyExpressionUtil {
@Nullable
@Override
+ public Boolean visitWildcardType(PsiWildcardType wildcardType) {
+ final PsiType bound = wildcardType.getBound();
+ if (bound != null) {
+ return bound.accept(this);
+ }
+ return false;
+ }
+
+ @Nullable
+ @Override
public Boolean visitClassType(PsiClassType classType) {
for (PsiType type : classType.getParameters()) {
if (type.accept(this)) return true;
@@ -105,12 +123,14 @@ public class PsiPolyExpressionUtil {
});
}
- private static boolean isAssignmentOrInvocationContext(PsiElement context) {
- return context instanceof PsiExpressionList || isAssignmentContext(context);
+ private static boolean isInAssignmentOrInvocationContext(PsiExpression expr) {
+ final PsiElement context = expr.getParent();
+ return context instanceof PsiExpressionList || isAssignmentContext(expr, context);
}
- private static boolean isAssignmentContext(PsiElement context) {
- return context instanceof PsiReturnStatement ||
+ private static boolean isAssignmentContext(PsiExpression expr, PsiElement context) {
+ return PsiUtil.isCondition(expr, context) ||
+ context instanceof PsiReturnStatement ||
context instanceof PsiAssignmentExpression ||
context instanceof PsiVariable;
}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ConstraintFormula.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ConstraintFormula.java
new file mode 100644
index 000000000000..992d875bb535
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ConstraintFormula.java
@@ -0,0 +1,27 @@
+/*
+ * 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.psi.impl.source.resolve.graphInference.constraints;
+
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
+
+import java.util.List;
+
+/**
+ * User: anna
+ */
+public interface ConstraintFormula {
+ boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints);
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java
new file mode 100644
index 000000000000..a0e6a767d8f2
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java
@@ -0,0 +1,107 @@
+/*
+ * 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.psi.impl.source.resolve.graphInference.constraints;
+
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
+import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
+import com.intellij.psi.util.TypeConversionUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * User: anna
+ */
+public class ExpressionCompatibilityConstraint implements ConstraintFormula {
+ private PsiExpression myExpression;
+ private PsiType myT;
+
+ public ExpressionCompatibilityConstraint(@NotNull PsiExpression expression, @NotNull PsiType type) {
+ myExpression = expression;
+ myT = type;
+ }
+
+ @Override
+ public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) {
+ if (session.isProperType(myT)) {
+ return TypeConversionUtil.areTypesAssignmentCompatible(myT, myExpression);
+ }
+ if (!PsiPolyExpressionUtil.isPolyExpression(myExpression)) {
+ final PsiType exprType = myExpression.getType();
+ if (exprType != null && !exprType.equals(PsiType.NULL)) {
+ constraints.add(new TypeCompatibilityConstraint(myT, exprType));
+ }
+ return true;
+ }
+ if (myExpression instanceof PsiParenthesizedExpression) {
+ final PsiExpression expression = ((PsiParenthesizedExpression)myExpression).getExpression();
+ if (expression != null) {
+ constraints.add(new ExpressionCompatibilityConstraint(expression, myT));
+ return true;
+ }
+ }
+
+ if (myExpression instanceof PsiConditionalExpression) {
+ final PsiExpression thenExpression = ((PsiConditionalExpression)myExpression).getThenExpression();
+ if (thenExpression != null) {
+ constraints.add(new ExpressionCompatibilityConstraint(thenExpression, myT));
+ }
+
+ final PsiExpression elseExpression = ((PsiConditionalExpression)myExpression).getElseExpression();
+ if (elseExpression != null) {
+ constraints.add(new ExpressionCompatibilityConstraint(elseExpression, myT));
+ }
+ return true;
+ }
+
+ if (myExpression instanceof PsiCallExpression) {
+ //todo
+ }
+
+ if (myExpression instanceof PsiMethodReferenceExpression) {
+ //todo
+ }
+
+ if (myExpression instanceof PsiLambdaExpression) {
+ constraints.add(new LambdaExpressionCompatibilityConstraint((PsiLambdaExpression)myExpression, myT));
+ return true;
+ }
+
+
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ExpressionCompatibilityConstraint that = (ExpressionCompatibilityConstraint)o;
+
+ if (!myExpression.equals(that.myExpression)) return false;
+ if (!myT.equals(that.myT)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = myExpression.hashCode();
+ result = 31 * result + myT.hashCode();
+ return result;
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java
new file mode 100644
index 000000000000..9d9dad46ffc8
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java
@@ -0,0 +1,75 @@
+package com.intellij.psi.impl.source.resolve.graphInference.constraints;
+
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable;
+import com.intellij.psi.util.PsiUtil;
+
+import java.util.List;
+
+/**
+ * User: anna
+ */
+public class LambdaExpressionCompatibilityConstraint implements ConstraintFormula {
+ private final PsiLambdaExpression myExpression;
+ private final PsiType myT;
+
+ public LambdaExpressionCompatibilityConstraint(PsiLambdaExpression expression, PsiType t) {
+ myExpression = expression;
+ myT = t;
+ }
+
+ @Override
+ public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) {
+ final InferenceVariable inferenceVariable = session.getInferenceVariable(myT);
+ if (inferenceVariable != null) {
+ delayedConstraints.add(this);
+ return true;
+ }
+ if (LambdaHighlightingUtil.checkInterfaceFunctional(myT) != null) {
+ return false;
+ }
+
+ if (myExpression.hasFormalParameterTypes()) {
+ }
+ final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(myT);
+ if (interfaceMethod == null) {
+ return false;
+ }
+ final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, PsiUtil.resolveGenericsClassInType(myT));
+ final PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters();
+ for (PsiParameter parameter : parameters) {
+ if (!session.isProperType(parameter.getType())) {
+ delayedConstraints.add(this);
+ return true;
+ }
+ }
+
+ final PsiParameter[] lambdaParameters = myExpression.getParameterList().getParameters();
+ if (lambdaParameters.length != parameters.length) {
+ return false;
+ }
+ if (myExpression.hasFormalParameterTypes()) {
+ for (int i = 0; i < lambdaParameters.length; i++) {
+ constraints.add(new TypeEqualityConstraint(lambdaParameters[i].getType(), substitutor.substitute(parameters[i].getType())));
+ }
+ }
+
+ final PsiType returnType = interfaceMethod.getReturnType();
+ if (returnType != null) {
+ if (returnType.equals(PsiType.VOID)) {
+ if (!myExpression.isVoidCompatible() && !(myExpression.getBody() instanceof PsiExpression)) {
+ return false;
+ }
+ } else {
+ if (myExpression.isVoidCompatible()) { //not value-compatible
+ return false;
+ }
+ for (PsiExpression returnExpressions : LambdaUtil.getReturnExpressions(myExpression)) {
+ constraints.add(new ExpressionCompatibilityConstraint(returnExpressions, returnType));
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/SubtypingConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/SubtypingConstraint.java
new file mode 100644
index 000000000000..64c28d1b462f
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/SubtypingConstraint.java
@@ -0,0 +1,175 @@
+/*
+ * 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.psi.impl.source.resolve.graphInference.constraints;
+
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceBound;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable;
+import com.intellij.psi.util.TypeConversionUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * User: anna
+ */
+public class SubtypingConstraint implements ConstraintFormula {
+ private PsiType myS;
+ private PsiType myT;
+ private boolean myIsRefTypes;
+
+ public SubtypingConstraint(PsiType t, PsiType s, boolean isRefTypes) {
+ myT = t;
+ myS = s;
+ myIsRefTypes = isRefTypes;
+ }
+
+ @Override
+ public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) {
+ if (myIsRefTypes) {
+ if (session.isProperType(myS) && session.isProperType(myT)) {
+ if (myT == null || myS == null) return myS == myT;
+ return TypeConversionUtil.isAssignable(myT, myS);
+ }
+ InferenceVariable inferenceVariable = session.getInferenceVariable(myS);
+ if (inferenceVariable != null) {
+ inferenceVariable.addBound(myT, InferenceBound.UPPER);
+ return true;
+ }
+ if (PsiType.NULL.equals(myS)) return true;
+ inferenceVariable = session.getInferenceVariable(myT);
+ if (inferenceVariable != null) {
+ inferenceVariable.addBound(myS, InferenceBound.LOWER);
+ return true;
+ }
+ if (myT instanceof PsiArrayType) {
+ if (!(myS instanceof PsiArrayType)) return false; //todo most specific array supertype
+ final PsiType tComponentType = ((PsiArrayType)myT).getComponentType();
+ final PsiType sComponentType = ((PsiArrayType)myS).getComponentType();
+ if (!(tComponentType instanceof PsiPrimitiveType) && !(sComponentType instanceof PsiPrimitiveType)) {
+ constraints.add(new SubtypingConstraint(tComponentType, sComponentType, true));
+ return true;
+ }
+ return sComponentType instanceof PsiPrimitiveType && sComponentType.equals(tComponentType);
+ }
+ if (myT instanceof PsiClassType) {
+ final PsiClassType.ClassResolveResult TResult = ((PsiClassType)myT).resolveGenerics();
+ final PsiClass CClass = TResult.getElement();
+ if (CClass != null) {
+ if (CClass instanceof PsiTypeParameter) {
+ if (myS instanceof PsiIntersectionType) {
+ for (PsiType conjunct : ((PsiIntersectionType)myS).getConjuncts()) {
+ if (myT.equals(conjunct)) return true;
+ }
+ }
+ //todo ((PsiTypeParameter)C).getLowerBound()
+ return false;
+ }
+
+ if (!(myS instanceof PsiClassType)) return false;
+ PsiClassType.ClassResolveResult SResult = ((PsiClassType)myS).resolveGenerics();
+ PsiClass SClass = SResult.getElement();
+ final PsiSubstitutor tSubstitutor = TResult.getSubstitutor();
+ final PsiSubstitutor sSubstitutor = SClass != null ? TypeConversionUtil.getClassSubstitutor(CClass, SClass, SResult.getSubstitutor()) : null;
+ if (sSubstitutor != null) {
+ for (PsiTypeParameter parameter : CClass.getTypeParameters()) {
+ final PsiType tSubstituted = tSubstitutor.substitute(parameter);
+ final PsiType sSubstituted = sSubstitutor.substituteWithBoundsPromotion(parameter);
+ constraints.add(new SubtypingConstraint(tSubstituted, sSubstituted, false));
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if (myT instanceof PsiIntersectionType) {
+ for (PsiType conjunct : ((PsiIntersectionType)myT).getConjuncts()) {
+ constraints.add(new SubtypingConstraint(conjunct, myS, true));
+ }
+ return true;
+ }
+
+ if (PsiType.NULL.equals(myT)) return false;
+ } else {
+ if (myT instanceof PsiWildcardType) {
+ final PsiType tBound = ((PsiWildcardType)myT).getBound();
+ if (tBound == null) {
+ return true;
+ }
+ if (((PsiWildcardType)myT).isExtends()) {
+ if (tBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
+ return true;
+ }
+ if (myS instanceof PsiWildcardType) {
+ final PsiType sBound = ((PsiWildcardType)myS).getBound();
+ if (sBound != null && ((PsiWildcardType)myS).isExtends()) {
+ constraints.add(new SubtypingConstraint(tBound, sBound, true));
+ return true;
+ }
+ } else {
+ constraints.add(new SubtypingConstraint(tBound, myS, true));
+ return true;
+ }
+ return false;
+ } else {
+ if (myS instanceof PsiWildcardType) {
+ final PsiType sBound = ((PsiWildcardType)myS).getBound();
+ if (sBound != null && ((PsiWildcardType)myS).isSuper()) {
+ constraints.add(new SubtypingConstraint(sBound, tBound, true));
+ return true;
+ }
+ } else {
+ constraints.add(new SubtypingConstraint(myS, tBound, true));
+ return true;
+ }
+ }
+ return false;
+ } else {
+ if (myS instanceof PsiWildcardType) {
+ return false;
+ } else {
+ constraints.add(new SubtypingConstraint(myT, myS, true));
+ return true;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ SubtypingConstraint that = (SubtypingConstraint)o;
+
+ if (myIsRefTypes != that.myIsRefTypes) return false;
+ if (myS != null ? !myS.equals(that.myS) : that.myS != null) return false;
+ if (myT != null ? !myT.equals(that.myT) : that.myT != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = myS != null ? myS.hashCode() : 0;
+ result = 31 * result + (myT != null ? myT.hashCode() : 0);
+ result = 31 * result + (myIsRefTypes ? 1 : 0);
+ return result;
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java
new file mode 100644
index 000000000000..353f03b66a80
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.psi.impl.source.resolve.graphInference.constraints;
+
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiPrimitiveType;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
+import com.intellij.psi.util.TypeConversionUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * User: anna
+ */
+public class TypeCompatibilityConstraint implements ConstraintFormula {
+ private PsiType myT;
+ private PsiType myS;
+
+ public TypeCompatibilityConstraint(@NotNull PsiType t, @NotNull PsiType s) {
+ myT = t;
+ myS = s;
+ }
+
+ @Override
+ public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) {
+ if (session.isProperType(myT) && session.isProperType(myS)) {
+ return TypeConversionUtil.isAssignable(myS, myT);
+ }
+ if (myS instanceof PsiPrimitiveType) {
+ final PsiClassType boxedType = ((PsiPrimitiveType)myS).getBoxedType(session.getManager(), session.getScope());
+ if (boxedType != null) {
+ constraints.add(new TypeCompatibilityConstraint(myT, boxedType));
+ return true;
+ }
+ }
+ if (myT instanceof PsiPrimitiveType) {
+ final PsiClassType boxedType = ((PsiPrimitiveType)myT).getBoxedType(session.getManager(), session.getScope());
+ if (boxedType != null) {
+ constraints.add(new TypeCompatibilityConstraint(boxedType, myS));
+ return true;
+ }
+ }
+ constraints.add(new SubtypingConstraint(myT, myS, true));
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeCompatibilityConstraint that = (TypeCompatibilityConstraint)o;
+
+ if (!myS.equals(that.myS)) return false;
+ if (!myT.equals(that.myT)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = myT.hashCode();
+ result = 31 * result + myS.hashCode();
+ return result;
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java
new file mode 100644
index 000000000000..a71465653a6f
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java
@@ -0,0 +1,124 @@
+/*
+ * 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.psi.impl.source.resolve.graphInference.constraints;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceBound;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
+import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * User: anna
+ */
+public class TypeEqualityConstraint implements ConstraintFormula {
+ private static final Logger LOG = Logger.getInstance("#" + TypeEqualityConstraint.class.getName());
+ private PsiType myT;
+ private PsiType myS;
+
+ public TypeEqualityConstraint(@NotNull PsiType t, @NotNull PsiType s) {
+ myT = t;
+ myS = s;
+ }
+
+ @Override
+ public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) {
+ if (session.isProperType(myT) && session.isProperType(myS)) {
+ return myT.equals(myS);
+ }
+ InferenceVariable inferenceVariable = session.getInferenceVariable(myS);
+ if (inferenceVariable != null) {
+ inferenceVariable.addBound(myT, InferenceBound.EQ);
+ return true;
+ }
+ inferenceVariable = session.getInferenceVariable(myT);
+ if (inferenceVariable != null) {
+ inferenceVariable.addBound(myS, InferenceBound.EQ);
+ return true;
+ }
+ if (myT instanceof PsiClassType && myS instanceof PsiClassType) {
+ final PsiClassType.ClassResolveResult tResult = ((PsiClassType)myT).resolveGenerics();
+ final PsiClassType.ClassResolveResult sResult = ((PsiClassType)myS).resolveGenerics();
+ final PsiClass C = tResult.getElement();
+ if (C == sResult.getElement() && C != null) {
+ final PsiSubstitutor tSubstitutor = tResult.getSubstitutor();
+ final PsiSubstitutor sSubstitutor = sResult.getSubstitutor();
+ for (PsiTypeParameter typeParameter : C.getTypeParameters()) {
+ final PsiType tSubstituted = tSubstitutor.substitute(typeParameter);
+ final PsiType sSubstituted = sSubstitutor.substitute(typeParameter);
+ if (tSubstituted != null && sSubstituted != null) {
+ constraints.add(new TypeEqualityConstraint(tSubstituted, sSubstituted));
+ }
+ }
+ return true;
+ }
+ }
+ if (myT instanceof PsiArrayType && myS instanceof PsiArrayType) {
+ constraints.add(new TypeEqualityConstraint(((PsiArrayType)myT).getComponentType(), ((PsiArrayType)myS).getComponentType()));
+ return true;
+ }
+ if (myT instanceof PsiIntersectionType && myS instanceof PsiIntersectionType) {
+ final PsiType[] tConjuncts = ((PsiIntersectionType)myT).getConjuncts();
+ final PsiType[] sConjuncts = ((PsiIntersectionType)myS).getConjuncts();
+ if (sConjuncts.length == tConjuncts.length) {
+ for (int i = 0; i < sConjuncts.length; i++) {
+ constraints.add(new TypeEqualityConstraint(tConjuncts[i], sConjuncts[i]));
+ }
+ return true;
+ }
+ }
+
+ if (myT instanceof PsiWildcardType && myS instanceof PsiWildcardType) {
+ final PsiType tBound = ((PsiWildcardType)myT).getBound();
+ final PsiType sBound = ((PsiWildcardType)myS).getBound();
+
+ if (tBound == null && sBound == null) return true;
+
+ if (((PsiWildcardType)myT).isExtends() && ((PsiWildcardType)myS).isExtends() ||
+ ((PsiWildcardType)myT).isSuper() && ((PsiWildcardType)myS).isSuper()) {
+
+ LOG.assertTrue(tBound != null);
+ LOG.assertTrue(sBound != null);
+ constraints.add(new TypeEqualityConstraint(tBound, sBound));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeEqualityConstraint that = (TypeEqualityConstraint)o;
+
+ if (!myS.equals(that.myS)) return false;
+ if (!myT.equals(that.myT)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = myT.hashCode();
+ result = 31 * result + myS.hashCode();
+ return result;
+ }
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java
new file mode 100644
index 000000000000..f6ba9fdf5af4
--- /dev/null
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java
@@ -0,0 +1,244 @@
+/*
+ * 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.psi.impl.source.tree;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.lang.PsiBuilder;
+import com.intellij.lang.java.parser.JavaParser;
+import com.intellij.lang.java.parser.JavaParserUtil;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.GeneratedMarkerVisitor;
+import com.intellij.psi.impl.source.*;
+import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.util.CharTable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author max
+ */
+public class JavaTreeGenerator implements TreeGenerator {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.JavaTreeGenerator");
+
+ private static final JavaParserUtil.ParserWrapper MOD_LIST = new JavaParserUtil.ParserWrapper() {
+ @Override
+ public void parse(final PsiBuilder builder) {
+ JavaParser.INSTANCE.getDeclarationParser().parseModifierList(builder);
+ }
+ };
+
+ @Override
+ @Nullable
+ public TreeElement generateTreeFor(PsiElement original, final CharTable table, final PsiManager manager) {
+ if (original instanceof PsiKeyword || original instanceof PsiIdentifier) {
+ final String text = original.getText();
+ return createLeafFromText(text, table, manager, original, ((PsiJavaToken)original).getTokenType());
+ }
+
+ if (original instanceof PsiModifierList) {
+ final String text = original.getText();
+ assert text != null : "Text is null for " + original + "; " + original.getClass();
+ final LanguageLevel level = PsiUtil.getLanguageLevel(original);
+ final DummyHolder holder = DummyHolderFactory.createHolder(original.getManager(), new JavaDummyElement(text, MOD_LIST, level), null);
+ final TreeElement modifierListElement = holder.getTreeElement().getFirstChildNode();
+ return markGeneratedIfNeeded(original, modifierListElement);
+ }
+
+ if (original instanceof PsiReferenceExpression) {
+ TreeElement element = createReferenceExpression(original.getProject(), original.getText(), original);
+ PsiElement refElement = ((PsiJavaCodeReferenceElement)original).resolve();
+ if (refElement instanceof PsiClass) {
+ element.putCopyableUserData(REFERENCED_CLASS_KEY, (PsiClass)refElement);
+ }
+ return element;
+ }
+
+ if (original instanceof PsiJavaCodeReferenceElement) {
+ PsiElement refElement = ((PsiJavaCodeReferenceElement)original).resolve();
+ final boolean generated = refElement != null && CodeEditUtil.isNodeGenerated(refElement.getNode());
+ if (refElement instanceof PsiClass) {
+ if (refElement instanceof PsiAnonymousClass) {
+ PsiJavaCodeReferenceElement ref = ((PsiAnonymousClass)refElement).getBaseClassReference();
+ original = ref;
+ refElement = ref.resolve();
+ }
+
+ boolean isFQ = false;
+ if (original instanceof PsiJavaCodeReferenceElementImpl) {
+ int kind = ((PsiJavaCodeReferenceElementImpl)original).getKind();
+ switch (kind) {
+ case PsiJavaCodeReferenceElementImpl.CLASS_OR_PACKAGE_NAME_KIND:
+ case PsiJavaCodeReferenceElementImpl.CLASS_NAME_KIND:
+ case PsiJavaCodeReferenceElementImpl.CLASS_IN_QUALIFIED_NEW_KIND:
+ isFQ = false;
+ break;
+
+ case PsiJavaCodeReferenceElementImpl.CLASS_FQ_NAME_KIND:
+ case PsiJavaCodeReferenceElementImpl.CLASS_FQ_OR_PACKAGE_NAME_KIND:
+ isFQ = true;
+ break;
+
+ default:
+ LOG.assertTrue(false);
+ }
+ }
+
+ final String text = isFQ ? ((PsiClass)refElement).getQualifiedName() : original.getText();
+ final TreeElement element = createReference(original.getProject(), text, generated);
+ element.putCopyableUserData(REFERENCED_CLASS_KEY, (PsiClass)refElement);
+ return element;
+ }
+ return createReference(original.getProject(), original.getText(), generated);
+ }
+
+ if (original instanceof PsiCompiledElement) {
+ PsiElement sourceVersion = original.getNavigationElement();
+ if (sourceVersion != original) {
+ return ChangeUtil.generateTreeElement(sourceVersion, table, manager);
+ }
+ PsiElement mirror = ((PsiCompiledElement)original).getMirror();
+ return ChangeUtil.generateTreeElement(mirror, table, manager);
+ }
+
+ if (original instanceof PsiTypeElement) {
+ PsiTypeElement typeElement = (PsiTypeElement)original;
+ PsiType type = typeElement.getType();
+
+ if (type instanceof PsiIntersectionType) {
+ type = ((PsiIntersectionType)type).getRepresentative();
+ }
+ else if (type instanceof PsiMethodReferenceType || type instanceof PsiLambdaExpressionType) {
+ type = PsiType.getJavaLangObject(manager, GlobalSearchScope.projectScope(manager.getProject()));
+ }
+
+ String text = type.getPresentableText();
+ PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(original.getProject()).getParserFacade();
+ PsiTypeElement element = parserFacade.createTypeElementFromText(text, original);
+
+ TreeElement result = (TreeElement)element.getNode();
+ markGeneratedIfNeeded(original, result);
+ encodeInfoInTypeElement(result, type);
+ return result;
+ }
+
+ return null;
+ }
+
+ private static LeafElement createLeafFromText(String text, CharTable table, PsiManager manager, PsiElement original, IElementType type) {
+ return Factory.createSingleLeafElement(type, text, 0, text.length(), table, manager, CodeEditUtil.isNodeGenerated(original.getNode()));
+ }
+
+ private static TreeElement markGeneratedIfNeeded(PsiElement original, TreeElement copy) {
+ if (CodeEditUtil.isNodeGenerated(original.getNode())) {
+ copy.acceptTree(new GeneratedMarkerVisitor());
+ }
+ return copy;
+ }
+
+ private static TreeElement createReference(final Project project, final String text, boolean mark) {
+ final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(project).getParserFacade();
+ final TreeElement element = (TreeElement)parserFacade.createReferenceFromText(text, null).getNode();
+ if (mark) element.acceptTree(new GeneratedMarkerVisitor());
+ return element;
+ }
+
+ private static TreeElement createReferenceExpression(final Project project, final String text, final PsiElement context) {
+ final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(project).getParserFacade();
+ final PsiExpression expression = parserFacade.createExpressionFromText(text, context);
+ return (TreeElement)expression.getNode();
+ }
+
+ private static void encodeInfoInTypeElement(ASTNode typeElement, PsiType type) {
+ if (type instanceof PsiPrimitiveType) return;
+ LOG.assertTrue(typeElement.getElementType() == JavaElementType.TYPE);
+ if (type instanceof PsiArrayType) {
+ final ASTNode firstChild = typeElement.getFirstChildNode();
+ LOG.assertTrue(firstChild.getElementType() == JavaElementType.TYPE);
+ encodeInfoInTypeElement(firstChild, ((PsiArrayType)type).getComponentType());
+ }
+ else if (type instanceof PsiWildcardType) {
+ final PsiType bound = ((PsiWildcardType)type).getBound();
+ if (bound == null) return;
+ final ASTNode lastChild = typeElement.getLastChildNode();
+ if (lastChild.getElementType() != JavaElementType.TYPE) return;
+ encodeInfoInTypeElement(lastChild, bound);
+ }
+ else if (type instanceof PsiCapturedWildcardType) {
+ final PsiType bound = ((PsiCapturedWildcardType)type).getWildcard().getBound();
+ if (bound == null) return;
+ final ASTNode lastChild = typeElement.getLastChildNode();
+ if (lastChild.getElementType() != JavaElementType.TYPE) return;
+ encodeInfoInTypeElement(lastChild, bound);
+ }
+ else if (type instanceof PsiIntersectionType) {
+ encodeInfoInTypeElement(typeElement, ((PsiIntersectionType)type).getRepresentative());
+ }
+ else if (type instanceof PsiClassType) {
+ final PsiClassType classType = (PsiClassType)type;
+ final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics();
+ PsiClass referencedClass = resolveResult.getElement();
+ if (referencedClass == null) return;
+ if (referencedClass instanceof PsiAnonymousClass) {
+ encodeInfoInTypeElement(typeElement, ((PsiAnonymousClass)referencedClass).getBaseClassType());
+ }
+ else {
+ final ASTNode reference = typeElement.findChildByType(JavaElementType.JAVA_CODE_REFERENCE);
+ // can be not the case for "? name"
+ if (reference instanceof CompositeElement) {
+ encodeClassTypeInfoInReference((CompositeElement)reference, resolveResult.getElement(), resolveResult.getSubstitutor());
+ }
+ }
+ }
+ }
+
+ private static void encodeClassTypeInfoInReference(@NotNull CompositeElement reference, PsiClass referencedClass, PsiSubstitutor substitutor) {
+ reference.putCopyableUserData(REFERENCED_CLASS_KEY, referencedClass);
+
+ final PsiTypeParameter[] typeParameters = referencedClass.getTypeParameters();
+ if (typeParameters.length == 0) return;
+
+ final ASTNode referenceParameterList = reference.findChildByRole(ChildRole.REFERENCE_PARAMETER_LIST);
+ int index = 0;
+ for (ASTNode child = referenceParameterList.getFirstChildNode(); child != null && index < typeParameters.length; child = child.getTreeNext()) {
+ if (child.getElementType() == JavaElementType.TYPE) {
+ final PsiType substitutedType = substitutor.substitute(typeParameters[index]);
+ if (substitutedType != null) {
+ encodeInfoInTypeElement(child, substitutedType);
+ }
+ index++;
+ }
+ }
+
+ final ASTNode qualifier = reference.findChildByRole(ChildRole.QUALIFIER);
+ if (qualifier != null) {
+ if (referencedClass.hasModifierProperty(PsiModifier.STATIC)) return;
+ final PsiClass outerClass = referencedClass.getContainingClass();
+ if (outerClass != null) {
+ encodeClassTypeInfoInReference((CompositeElement)qualifier, outerClass, substitutor);
+ }
+ }
+ }
+
+ static final Key<PsiClass> REFERENCED_CLASS_KEY = Key.create("REFERENCED_CLASS_KEY");
+ static final Key<PsiMember> REFERENCED_MEMBER_KEY = Key.create("REFERENCED_MEMBER_KEY");
+}
diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java
index 1b857c421770..46788578dee6 100644
--- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java
+++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java
@@ -323,7 +323,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
argumentList != null ? argumentList.getExpressionTypes() : null, getTypeArguments(),
getLanguageLevel()) {
@Override
- public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy) {
+ public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) {
return inferTypeArgumentsFromInterfaceMethod(signature, interfaceMethodReturnType, method, substitutor, languageLevel);
}
};
diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java
index 2ac053285b78..27e4227b5afb 100644
--- a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java
+++ b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java
@@ -240,7 +240,8 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
PsiMethod existingMethod = (PsiMethod)existing.getElement();
assert existingMethod != null;
PsiClass existingClass = existingMethod.getContainingClass();
- if (class1.isInterface() && CommonClassNames.JAVA_LANG_OBJECT.equals(existingClass.getQualifiedName())) { //prefer interface methods to methods from Object
+ if (class1 != null && existingClass != null &&
+ class1.isInterface() && CommonClassNames.JAVA_LANG_OBJECT.equals(existingClass.getQualifiedName())) { //prefer interface methods to methods from Object
signatures.put(signature, info);
continue;
}