RecordExhaustivenessResult (@NotNull List<? extends PsiCaseLabelElement> caseElements) { List<PsiPrimaryPattern> unconditionalPatterns = ContainerUtil.mapNotNull(caseElements, element -> JavaPsiPatternUtil.findUnconditionalPattern(element)); List<PsiDeconstructionPattern> unconditionalDeconstructions = ContainerUtil.filterIsInstance(unconditionalPatterns, PsiDeconstructionPattern.class); if (unconditionalDeconstructions.isEmpty()) { return RecordExhaustivenessResult.createExhaustiveResult(); //no deconstruction } PsiDeconstructionPattern scope = unconditionalDeconstructions.get(0); MultiMap<PsiType, PsiDeconstructionPattern> deconstructionGroups = ContainerUtil.groupBy(unconditionalDeconstructions, deconstruction -> JavaPsiPatternUtil.getPatternType(deconstruction)); MultiMap<PsiType, PsiTypeTestPattern> typeTestPatterns = ContainerUtil.groupBy( ContainerUtil.filterIsInstance(unconditionalPatterns, PsiTypeTestPattern.class), pattern -> JavaPsiPatternUtil.getPatternType(pattern)); RecordExhaustivenessResult result = RecordExhaustivenessResult.createExhaustiveResult(); for (Map.Entry<PsiType, Collection<PsiDeconstructionPattern>> entry : deconstructionGroups.entrySet()) { PsiType type = entry.getKey(); if (type == null) continue; Collection<PsiTypeTestPattern> patterns = typeTestPatterns.get(type); if (!patterns.isEmpty()) { for (PsiPrimaryPattern pattern : patterns) { //check, there is a non-deconstruction pattern, which cover it if (JavaPsiPatternUtil.isUnconditionalForType(pattern, type)) { return RecordExhaustivenessResult.createExhaustiveResult(); } } } ClassResolveResult resolve = PsiUtil.resolveGenericsClassInType(PsiUtil.captureToplevelWildcards(type, scope)); PsiClass selectorClass = resolve.getElement(); PsiSubstitutor substitutor = resolve.getSubstitutor(); if (selectorClass == null) continue; List<PsiType> recordTypes = ContainerUtil.map(selectorClass.getRecordComponents(), component -> substitutor.substitute(component.getType())); List<List<PsiPattern>> deconstructionComponentsGroup = ContainerUtil.map(entry.getValue(), deconstruction -> Arrays.asList( deconstruction.getDeconstructionList().getDeconstructionComponents())); if (ContainerUtil.exists(deconstructionComponentsGroup, group -> group.size() != recordTypes.size())) { return RecordExhaustivenessResult.createExhaustiveResult(); //it checked before, don't repeat error } RecordExhaustivenessResult currentResult = findExhaustiveInGroup(type, recordTypes, deconstructionComponentsGroup); result.merge(currentResult); } return result; }