<commit_before>// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.impl

import com.intellij.codeInsight.JavaModuleSystemEx
import com.intellij.codeInsight.JavaModuleSystemEx.ErrorWithFixes
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.codeInsight.daemon.JavaErrorMessages
import com.intellij.codeInsight.daemon.QuickFixBundle
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil
import com.intellij.codeInsight.daemon.impl.quickfix.AddRequiredModuleFix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.compiler.CompilerConfiguration
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.vfs.jrt.JrtFileSystem
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightJavaModule
import com.intellij.psi.impl.source.PsiJavaModuleReference
import com.intellij.psi.util.PsiUtil

/**
 * Checks package accessibility according to JLS 7 "Packages and Modules".
 *
 * @see <a href="https://docs.oracle.com/javase/specs/jls/se9/html/jls-7.html">JLS 7 "Packages and Modules"</a>
 * @see <a href="http://openjdk.java.net/jeps/261">JEP 261: Module System</a>
 */
class JavaPlatformModuleSystem : JavaModuleSystemEx {
  override fun getName() = "Java Platform Module System"

  override fun isAccessible(target: PsiPackage, place: PsiElement) = checkAccess(target, place, quick = true) == null
  override fun isAccessible(target: PsiClass, place: PsiElement) = checkAccess(target, place, quick = true) == null

  override fun checkAccess(target: PsiPackage, place: PsiElement) = checkAccess(target, place, quick = false)
  override fun checkAccess(target: PsiClass, place: PsiElement) = checkAccess(target, place, quick = false)

  private fun checkAccess(target: PsiClass, place: PsiElement, quick: Boolean): ErrorWithFixes? {
    val useFile = place.containingFile?.originalFile
    if (useFile != null && PsiUtil.isLanguageLevel9OrHigher(useFile)) {
      val targetFile = target.containingFile
      if (targetFile is PsiClassOwner) {
        return checkAccess(targetFile, useFile, targetFile.packageName, quick)
      }
    }

    return null
  }

  private fun checkAccess(target: PsiPackage, place: PsiElement, quick: Boolean): ErrorWithFixes? {
    val useFile = place.containingFile?.originalFile
    if (useFile != null && PsiUtil.isLanguageLevel9OrHigher(useFile)) {
      val useVFile = useFile.virtualFile
      if (useVFile != null) {
        val index = ProjectFileIndex.getInstance(useFile.project)
        val module = index.getModuleForFile(useVFile)
        if (module != null) {
          val test = index.isInTestSourceContent(useVFile)
          val dirs = target.getDirectories(module.getModuleWithDependenciesAndLibrariesScope(test))
          if (dirs.isEmpty()) {
            return if (quick) ERR else ErrorWithFixes(JavaErrorMessages.message("package.not.found", target.qualifiedName))
          }
          val error = checkAccess(dirs[0], useFile, target.qualifiedName, quick)
          return when {
            error == null -> null
            dirs.size == 1 -> error
            dirs.asSequence().drop(1).any { checkAccess(it, useFile, target.qualifiedName, true) == null } -> null
            else -> error
          }
        }
      }
    }

    return null
  }

  private val ERR = ErrorWithFixes("-")

  private fun checkAccess(target: PsiFileSystemItem, place: PsiFileSystemItem, packageName: String, quick: Boolean): ErrorWithFixes? {
    val targetModule = JavaModuleGraphUtil.findDescriptorByElement(target)?.originalElement as PsiJavaModule?
    val useModule = JavaModuleGraphUtil.findDescriptorByElement(place)?.originalElement as PsiJavaModule?

    if (targetModule != null) {
      if (targetModule == useModule) {
        return null
      }

      if (useModule == null && targetModule.containingFile?.virtualFile?.fileSystem !is JrtFileSystem) {
        return null  // a target is not on the mandatory module path
      }

      val targetName = targetModule.name
      val useName = useModule?.name ?: "ALL-UNNAMED"
      val module = place.virtualFile?.let { ProjectFileIndex.getInstance(place.project).getModuleForFile(it) }

      if (!(targetModule is LightJavaModule ||
            JavaModuleGraphUtil.exports(targetModule, packageName, useModule) ||
            module != null && inAddedExports(module, targetName, packageName, useName))) {
        if (quick) return ERR
        val fixes = if (module == null) emptyList() else listOf(AddExportsOptionFix(module, targetName, packageName, useName))
        return when (useModule) {
          null -> ErrorWithFixes(JavaErrorMessages.message("module.access.from.unnamed", packageName, targetName), fixes)
          else -> ErrorWithFixes(JavaErrorMessages.message("module.access.from.named", packageName, targetName, useName), fixes)
        }
      }

      if (useModule == null) {
        if (!targetName.startsWith("java.")) return null
        val root = PsiJavaModuleReference.resolve(place, "java.se", false)
        if (root == null || JavaModuleGraphUtil.reads(root, targetModule)) return null
        if (module != null && inAddedModules(module, targetName)) return null
        val fixes = if (quick || module == null) emptyList() else listOf(AddModulesOptionFix(module, targetName))
        return if (quick) ERR else ErrorWithFixes(JavaErrorMessages.message("module.access.not.in.graph", packageName, targetName), fixes)
      }

      if (!(targetName == PsiJavaModule.JAVA_BASE || JavaModuleGraphUtil.reads(useModule, targetModule))) {
        return if (quick) ERR else ErrorWithFixes(
          JavaErrorMessages.message("module.access.does.not.read", packageName, targetName, useName),
          listOf(AddRequiredModuleFix(useModule, targetName)))
      }
    }
    else if (useModule != null) {
      return if (quick) ERR else ErrorWithFixes(JavaErrorMessages.message("module.access.to.unnamed", packageName, useModule.name))
    }

    return null
  }

  private fun inAddedExports(module: Module, targetName: String, packageName: String, useName: String): Boolean {
    val options = CompilerConfiguration.getInstance(module.project).getAdditionalOptions(module)
    if (options.isEmpty()) return false
    val prefix = "${targetName}/${packageName}="
    return optionValues(options, "--add-exports")
      .filter { it.startsWith(prefix) }
      .map { it.substring(prefix.length) }
      .flatMap { it.splitToSequence(",") }
      .any { it == useName }
  }

  private fun inAddedModules(module: Module, moduleName: String): Boolean {
    val options = CompilerConfiguration.getInstance(module.project).getAdditionalOptions(module)
    return optionValues(options, "--add-modules")
      .flatMap { it.splitToSequence(",") }
      .any { it == moduleName || it == "ALL-SYSTEM" }
  }

  private fun optionValues(options: List<String>, name: String) =
    if (options.isEmpty()) emptySequence()
    else {
      var useValue = false
      options.asSequence()
        .filter { it.startsWith(name) }
        .map {
          when {
            it.length == name.length -> { useValue = true; "" }
            useValue -> { useValue = false; it }
            it[name.length] == '=' -> it.substring(name.length + 1)<commit_msg>[java] recognizes '--add-modules <module>' option format (IDEA-181150)<commit_after>// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.impl

import com.intellij.codeInsight.JavaModuleSystemEx
import com.intellij.codeInsight.JavaModuleSystemEx.ErrorWithFixes
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.codeInsight.daemon.JavaErrorMessages
import com.intellij.codeInsight.daemon.QuickFixBundle
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil
import com.intellij.codeInsight.daemon.impl.quickfix.AddRequiredModuleFix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.compiler.CompilerConfiguration
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.vfs.jrt.JrtFileSystem
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightJavaModule
import com.intellij.psi.impl.source.PsiJavaModuleReference
import com.intellij.psi.util.PsiUtil

/**
 * Checks package accessibility according to JLS 7 "Packages and Modules".
 *
 * @see <a href="https://docs.oracle.com/javase/specs/jls/se9/html/jls-7.html">JLS 7 "Packages and Modules"</a>
 * @see <a href="http://openjdk.java.net/jeps/261">JEP 261: Module System</a>
 */
class JavaPlatformModuleSystem : JavaModuleSystemEx {
  override fun getName() = "Java Platform Module System"

  override fun isAccessible(target: PsiPackage, place: PsiElement) = checkAccess(target, place, quick = true) == null
  override fun isAccessible(target: PsiClass, place: PsiElement) = checkAccess(target, place, quick = true) == null

  override fun checkAccess(target: PsiPackage, place: PsiElement) = checkAccess(target, place, quick = false)
  override fun checkAccess(target: PsiClass, place: PsiElement) = checkAccess(target, place, quick = false)

  private fun checkAccess(target: PsiClass, place: PsiElement, quick: Boolean): ErrorWithFixes? {
    val useFile = place.containingFile?.originalFile
    if (useFile != null && PsiUtil.isLanguageLevel9OrHigher(useFile)) {
      val targetFile = target.containingFile
      if (targetFile is PsiClassOwner) {
        return checkAccess(targetFile, useFile, targetFile.packageName, quick)
      }
    }

    return null
  }

  private fun checkAccess(target: PsiPackage, place: PsiElement, quick: Boolean): ErrorWithFixes? {
    val useFile = place.containingFile?.originalFile
    if (useFile != null && PsiUtil.isLanguageLevel9OrHigher(useFile)) {
      val useVFile = useFile.virtualFile
      if (useVFile != null) {
        val index = ProjectFileIndex.getInstance(useFile.project)
        val module = index.getModuleForFile(useVFile)
        if (module != null) {
          val test = index.isInTestSourceContent(useVFile)
          val dirs = target.getDirectories(module.getModuleWithDependenciesAndLibrariesScope(test))
          if (dirs.isEmpty()) {
            return if (quick) ERR else ErrorWithFixes(JavaErrorMessages.message("package.not.found", target.qualifiedName))
          }
          val error = checkAccess(dirs[0], useFile, target.qualifiedName, quick)
          return when {
            error == null -> null
            dirs.size == 1 -> error
            dirs.asSequence().drop(1).any { checkAccess(it, useFile, target.qualifiedName, true) == null } -> null
            else -> error
          }
        }
      }
    }

    return null
  }

  private val ERR = ErrorWithFixes("-")

  private fun checkAccess(target: PsiFileSystemItem, place: PsiFileSystemItem, packageName: String, quick: Boolean): ErrorWithFixes? {
    val targetModule = JavaModuleGraphUtil.findDescriptorByElement(target)?.originalElement as PsiJavaModule?
    val useModule = JavaModuleGraphUtil.findDescriptorByElement(place)?.originalElement as PsiJavaModule?

    if (targetModule != null) {
      if (targetModule == useModule) {
        return null
      }

      if (useModule == null && targetModule.containingFile?.virtualFile?.fileSystem !is JrtFileSystem) {
        return null  // a target is not on the mandatory module path
      }

      val targetName = targetModule.name
      val useName = useModule?.name ?: "ALL-UNNAMED"
      val module = place.virtualFile?.let { ProjectFileIndex.getInstance(place.project).getModuleForFile(it) }

      if (!(targetModule is LightJavaModule ||
            JavaModuleGraphUtil.exports(targetModule, packageName, useModule) ||
            module != null && inAddedExports(module, targetName, packageName, useName))) {
        if (quick) return ERR
        val fixes = if (module == null) emptyList() else listOf(AddExportsOptionFix(module, targetName, packageName, useName))
        return when (useModule) {
          null -> ErrorWithFixes(JavaErrorMessages.message("module.access.from.unnamed", packageName, targetName), fixes)
          else -> ErrorWithFixes(JavaErrorMessages.message("module.access.from.named", packageName, targetName, useName), fixes)
        }
      }

      if (useModule == null) {
        if (!targetName.startsWith("java.")) return null
        val root = PsiJavaModuleReference.resolve(place, "java.se", false)
        if (root == null || JavaModuleGraphUtil.reads(root, targetModule)) return null
        if (module != null && inAddedModules(module, targetName)) return null
        val fixes = if (quick || module == null) emptyList() else listOf(AddModulesOptionFix(module, targetName))
        return if (quick) ERR else ErrorWithFixes(JavaErrorMessages.message("module.access.not.in.graph", packageName, targetName), fixes)
      }

      if (!(targetName == PsiJavaModule.JAVA_BASE || JavaModuleGraphUtil.reads(useModule, targetModule))) {
        return if (quick) ERR else ErrorWithFixes(
          JavaErrorMessages.message("module.access.does.not.read", packageName, targetName, useName),
          listOf(AddRequiredModuleFix(useModule, targetName)))
      }
    }
    else if (useModule != null) {
      return if (quick) ERR else ErrorWithFixes(JavaErrorMessages.message("module.access.to.unnamed", packageName, useModule.name))
    }

    return null
  }

  private fun inAddedExports(module: Module, targetName: String, packageName: String, useName: String): Boolean {
    val options = CompilerConfiguration.getInstance(module.project).getAdditionalOptions(module)
    if (options.isEmpty()) return false
    val prefix = "${targetName}/${packageName}="
    return optionValues(options, "--add-exports")
      .filter { it.startsWith(prefix) }
      .map { it.substring(prefix.length) }
      .flatMap { it.splitToSequence(",") }
      .any { it == useName }
  }

  private fun inAddedModules(module: Module, moduleName: String): Boolean {
    val options = CompilerConfiguration.getInstance(module.project).getAdditionalOptions(module)
    return optionValues(options, "--add-modules")
      .flatMap { it.splitToSequence(",") }
      .any { it == moduleName || it == "ALL-SYSTEM" }
  }

  private fun optionValues(options: List<String>, name: String) =
    if (options.isEmpty()) emptySequence()
    else {
      var useValue = false
      options.asSequence()
        .map {
          when {
            it == name -> { useValue = true; "" }
            useValue -> { useValue = false; it }
            it.startsWith(name) && it[name.length] == '=' -> it.substring(name.length + 1)
{'fraction_non_alphanumeric': 0.11077844311377245, 'fraction_numerical': 0.00385894876912841, 'mean_word_length': 3.682554517133956, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 32, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2770063', 'n_tokens_mistral': 4429, 'n_tokens_neox': 4319, 'n_words': 1004}