Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Features/#384 inspections are enabled for workflows #391

Merged
merged 5 commits into from
Jul 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 100 additions & 75 deletions src/main/kotlin/com/jetbrains/snakecharm/codeInsight/SnakemakeAPI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ import com.jetbrains.snakecharm.lang.SnakemakeNames.SNAKEMAKE_IO_METHOD_REPORT
import com.jetbrains.snakecharm.lang.SnakemakeNames.SNAKEMAKE_IO_METHOD_TEMP
import com.jetbrains.snakecharm.lang.SnakemakeNames.SNAKEMAKE_IO_METHOD_TOUCH
import com.jetbrains.snakecharm.lang.SnakemakeNames.SNAKEMAKE_IO_METHOD_UNPACK
import com.jetbrains.snakecharm.lang.SnakemakeNames.WORKFLOW_CONTAINERIZED_KEYWORD
import com.jetbrains.snakecharm.lang.SnakemakeNames.WORKFLOW_CONTAINER_KEYWORD
import com.jetbrains.snakecharm.lang.SnakemakeNames.WORKFLOW_SINGULARITY_KEYWORD

/**
* Also see [ImplicitPySymbolsProvider] class
Expand All @@ -59,7 +62,7 @@ object SnakemakeAPI {
)

val FUNCTIONS_BANNED_FOR_WILDCARDS = listOf(
SMK_FUN_EXPAND
SMK_FUN_EXPAND
)

const val SMK_VARS_WILDCARDS = "wildcards"
Expand All @@ -69,53 +72,65 @@ object SnakemakeAPI {
* Also see [ImplicitPySymbolsProvider], it also processes 'InputFiles', etc. symbols
*/
val SECTION_ACCESSOR_CLASSES = mapOf(
"snakemake.io.InputFiles" to "input",
"snakemake.io.OutputFiles" to "output",
"snakemake.io.Params" to "params",
"snakemake.io.Log" to "log",
"snakemake.io.Resources" to "resources"
"snakemake.io.InputFiles" to "input",
"snakemake.io.OutputFiles" to "output",
"snakemake.io.Params" to "params",
"snakemake.io.Log" to "log",
"snakemake.io.Resources" to "resources"
)
const val SNAKEMAKE_MODULE_NAME_IO_PY = "io.py"

val EXECUTION_SECTIONS_KEYWORDS = setOf(
SECTION_SHELL, SECTION_SCRIPT,
SECTION_WRAPPER, SECTION_CWL, SECTION_NOTEBOOK
SECTION_SHELL, SECTION_SCRIPT,
SECTION_WRAPPER, SECTION_CWL, SECTION_NOTEBOOK
)

/**
* Rule or checkpoint sections that allows only single argument
*/
val SINGLE_ARGUMENT_SECTIONS_KEYWORDS = setOf(
SECTION_SHELL, SECTION_SCRIPT, SECTION_WRAPPER,
SECTION_CWL, SECTION_BENCHMARK, SECTION_VERSION,
SECTION_MESSAGE, SECTION_THREADS, SECTION_SINGULARITY,
SECTION_PRIORITY, SECTION_CONDA, SECTION_GROUP,
SECTION_SHADOW, SECTION_CACHE, SECTION_NOTEBOOK, SECTION_CONTAINER,
SECTION_HANDOVER, SECTION_CONTAINERIZED
SECTION_SHELL, SECTION_SCRIPT, SECTION_WRAPPER,
SECTION_CWL, SECTION_BENCHMARK, SECTION_VERSION,
SECTION_MESSAGE, SECTION_THREADS, SECTION_SINGULARITY,
SECTION_PRIORITY, SECTION_CONDA, SECTION_GROUP,
SECTION_SHADOW, SECTION_CACHE, SECTION_NOTEBOOK, SECTION_CONTAINER,
SECTION_HANDOVER, SECTION_CONTAINERIZED
)

/**
* Workflow top-level sections that allows only single argument
*/
val SINGLE_ARGUMENT_WORKFLOWS_KEYWORDS = setOf(
WORKFLOW_CONTAINERIZED_KEYWORD, WORKFLOW_CONTAINER_KEYWORD,
WORKFLOW_SINGULARITY_KEYWORD
)

/**
* For rules parsing
*/
val RULE_OR_CHECKPOINT_ARGS_SECTION_KEYWORDS = setOf(
SECTION_OUTPUT, SECTION_INPUT, SECTION_PARAMS, SECTION_LOG, SECTION_RESOURCES,
SECTION_BENCHMARK, SECTION_VERSION, SECTION_MESSAGE, SECTION_SHELL, SECTION_THREADS, SECTION_SINGULARITY,
SECTION_PRIORITY, SECTION_WILDCARD_CONSTRAINTS, SECTION_GROUP, SECTION_SHADOW,
SECTION_CONDA,
SECTION_SCRIPT, SECTION_WRAPPER, SECTION_CWL, SECTION_NOTEBOOK,
SECTION_CACHE,
SECTION_CONTAINER,
SECTION_CONTAINERIZED,
SECTION_ENVMODULES,
SECTION_NAME,
SECTION_HANDOVER
)
val RULE_OR_CHECKPOINT_SECTION_KEYWORDS = (RULE_OR_CHECKPOINT_ARGS_SECTION_KEYWORDS + setOf(SnakemakeNames.SECTION_RUN))
SECTION_OUTPUT, SECTION_INPUT, SECTION_PARAMS, SECTION_LOG, SECTION_RESOURCES,
SECTION_BENCHMARK, SECTION_VERSION, SECTION_MESSAGE, SECTION_SHELL, SECTION_THREADS, SECTION_SINGULARITY,
SECTION_PRIORITY, SECTION_WILDCARD_CONSTRAINTS, SECTION_GROUP, SECTION_SHADOW,
SECTION_CONDA,
SECTION_SCRIPT, SECTION_WRAPPER, SECTION_CWL, SECTION_NOTEBOOK,
SECTION_CACHE,
SECTION_CONTAINER,
SECTION_CONTAINERIZED,
SECTION_ENVMODULES,
SECTION_NAME,
SECTION_HANDOVER
)
val RULE_OR_CHECKPOINT_SECTION_KEYWORDS =
(RULE_OR_CHECKPOINT_ARGS_SECTION_KEYWORDS + setOf(SnakemakeNames.SECTION_RUN))

/**
* For subworkflows parsing
*/
val SUBWORKFLOW_SECTIONS_KEYWORDS = setOf(
SnakemakeNames.SUBWORKFLOW_WORKDIR_KEYWORD,
SnakemakeNames.SUBWORKFLOW_SNAKEFILE_KEYWORD,
SnakemakeNames.SUBWORKFLOW_CONFIGFILE_KEYWORD
SnakemakeNames.SUBWORKFLOW_WORKDIR_KEYWORD,
SnakemakeNames.SUBWORKFLOW_SNAKEFILE_KEYWORD,
SnakemakeNames.SUBWORKFLOW_CONFIGFILE_KEYWORD
)

/**
Expand All @@ -124,18 +139,18 @@ object SnakemakeAPI {
* to filter these sections for resolve and completion
*/
val RULE_TYPE_ACCESSIBLE_SECTIONS = setOf(
SECTION_INPUT,
SECTION_LOG,
SECTION_OUTPUT,
SECTION_PARAMS,
SECTION_RESOURCES,
SECTION_VERSION,
SECTION_INPUT,
SECTION_LOG,
SECTION_OUTPUT,
SECTION_PARAMS,
SECTION_RESOURCES,
SECTION_VERSION,

SECTION_MESSAGE,
SECTION_WILDCARD_CONSTRAINTS,
SECTION_BENCHMARK,
SECTION_PRIORITY,
SECTION_WRAPPER
SECTION_MESSAGE,
SECTION_WILDCARD_CONSTRAINTS,
SECTION_BENCHMARK,
SECTION_PRIORITY,
SECTION_WRAPPER
)

/**
Expand All @@ -144,21 +159,21 @@ object SnakemakeAPI {
* expand wildcards.
*/
val SMK_SL_INITIAL_TYPE_ACCESSIBLE_SECTIONS = setOf(
SECTION_INPUT,
SECTION_OUTPUT, SECTION_LOG,
SECTION_THREADS, SECTION_PARAMS,
SECTION_RESOURCES,
SECTION_VERSION
SECTION_INPUT,
SECTION_OUTPUT, SECTION_LOG,
SECTION_THREADS, SECTION_PARAMS,
SECTION_RESOURCES,
SECTION_VERSION
)

val SECTIONS_INVALID_FOR_INJECTION = setOf(
SECTION_WILDCARD_CONSTRAINTS,
SECTION_SHADOW,
SECTION_WRAPPER,
SECTION_VERSION, SECTION_THREADS,
SECTION_PRIORITY, SECTION_SINGULARITY, SECTION_CACHE,
SECTION_CONTAINER, SECTION_CONTAINERIZED, SECTION_NOTEBOOK,
SECTION_ENVMODULES, SECTION_HANDOVER
SECTION_WILDCARD_CONSTRAINTS,
SECTION_SHADOW,
SECTION_WRAPPER,
SECTION_VERSION, SECTION_THREADS,
SECTION_PRIORITY, SECTION_SINGULARITY, SECTION_CACHE,
SECTION_CONTAINER, SECTION_CONTAINERIZED, SECTION_NOTEBOOK,
SECTION_ENVMODULES, SECTION_HANDOVER
)

/**
Expand All @@ -167,9 +182,9 @@ object SnakemakeAPI {
* TODO: Consider implementing this as PSI interface in order not to compare keyword string each time
*/
val WILDCARDS_EXPANDING_SECTIONS_KEYWORDS = setOf(
SECTION_INPUT, SECTION_OUTPUT, SECTION_CONDA,
SECTION_RESOURCES, SECTION_GROUP, SECTION_BENCHMARK,
SECTION_LOG, SECTION_PARAMS
SECTION_INPUT, SECTION_OUTPUT, SECTION_CONDA,
SECTION_RESOURCES, SECTION_GROUP, SECTION_BENCHMARK,
SECTION_LOG, SECTION_PARAMS
)

/**
Expand All @@ -178,7 +193,7 @@ object SnakemakeAPI {
* TODO: Consider implementing this as PSI interface in order not to compare keyword string each time
*/
val WILDCARDS_DEFINING_SECTIONS_KEYWORDS = listOf(
SECTION_OUTPUT, SECTION_LOG, SECTION_BENCHMARK
SECTION_OUTPUT, SECTION_LOG, SECTION_BENCHMARK
)

/**
Expand Down Expand Up @@ -206,31 +221,41 @@ object SnakemakeAPI {
SMK_VARS_ATTEMPT
)
)
val SECTION_LAMBDA_ARG_POSSIBLE_PARAMS: Set<String> = ALLOWED_LAMBDA_OR_CALLABLE_ARGS.values.flatMap { it.asIterable() }.toMutableSet().also {
it.addAll(RULE_OR_CHECKPOINT_ARGS_SECTION_KEYWORDS)
}
val SECTION_LAMBDA_ARG_POSSIBLE_PARAMS: Set<String> =
ALLOWED_LAMBDA_OR_CALLABLE_ARGS.values.flatMap { it.asIterable() }.toMutableSet().also {
it.addAll(RULE_OR_CHECKPOINT_ARGS_SECTION_KEYWORDS)
}

/**
* Set of rule\checkpoint sections that does not expect keyword arguments
* Rule/checkpoint sections that does not allow keyword arguments
*/
val SECTIONS_WHERE_KEYWORD_ARGS_PROHIBITED = setOf(
SECTION_BENCHMARK, SECTION_VERSION, SECTION_MESSAGE, SECTION_SHELL, SECTION_THREADS, SECTION_SINGULARITY,
SECTION_PRIORITY, SECTION_GROUP, SECTION_SHADOW, SECTION_CONDA, SECTION_SCRIPT, SECTION_WRAPPER,
SECTION_CWL, SECTION_NOTEBOOK, SECTION_CACHE, SECTION_CONTAINER, SECTION_CONTAINERIZED, SECTION_ENVMODULES,
SECTION_NAME, SECTION_HANDOVER
SECTION_BENCHMARK, SECTION_VERSION, SECTION_MESSAGE, SECTION_SHELL, SECTION_THREADS, SECTION_SINGULARITY,
SECTION_PRIORITY, SECTION_GROUP, SECTION_SHADOW, SECTION_CONDA, SECTION_SCRIPT, SECTION_WRAPPER,
SECTION_CWL, SECTION_NOTEBOOK, SECTION_CACHE, SECTION_CONTAINER, SECTION_CONTAINERIZED, SECTION_ENVMODULES,
SECTION_NAME, SECTION_HANDOVER
)


/**
* Workflow top-level sections that does not allow keyword args
*/
val WORKFLOWS_WHERE_KEYWORD_ARGS_PROHIBITED = setOf(
WORKFLOW_CONTAINERIZED_KEYWORD, WORKFLOW_CONTAINER_KEYWORD,
WORKFLOW_SINGULARITY_KEYWORD
)

val IO_FLAG_2_SUPPORTED_SECTION: HashMap<String, List<String>> = hashMapOf(
SNAKEMAKE_IO_METHOD_ANCIENT to listOf(SECTION_INPUT),
SNAKEMAKE_IO_METHOD_PROTECTED to listOf(SECTION_OUTPUT, SECTION_LOG, SECTION_BENCHMARK),
SNAKEMAKE_IO_METHOD_DIRECTORY to listOf(SECTION_OUTPUT),
SNAKEMAKE_IO_METHOD_REPORT to listOf(SECTION_OUTPUT),
SNAKEMAKE_IO_METHOD_TEMP to listOf(SECTION_INPUT, SECTION_OUTPUT),
SNAKEMAKE_IO_METHOD_TOUCH to listOf(SECTION_OUTPUT, SECTION_LOG, SECTION_BENCHMARK),
SNAKEMAKE_IO_METHOD_PIPE to listOf(SECTION_OUTPUT),
SNAKEMAKE_IO_METHOD_REPEAT to listOf(SECTION_BENCHMARK),
SNAKEMAKE_IO_METHOD_UNPACK to listOf(SECTION_INPUT),
SNAKEMAKE_IO_METHOD_DYNAMIC to listOf(SECTION_OUTPUT)
SNAKEMAKE_IO_METHOD_ANCIENT to listOf(SECTION_INPUT),
SNAKEMAKE_IO_METHOD_PROTECTED to listOf(SECTION_OUTPUT, SECTION_LOG, SECTION_BENCHMARK),
SNAKEMAKE_IO_METHOD_DIRECTORY to listOf(SECTION_OUTPUT),
SNAKEMAKE_IO_METHOD_REPORT to listOf(SECTION_OUTPUT),
SNAKEMAKE_IO_METHOD_TEMP to listOf(SECTION_INPUT, SECTION_OUTPUT),
SNAKEMAKE_IO_METHOD_TOUCH to listOf(SECTION_OUTPUT, SECTION_LOG, SECTION_BENCHMARK),
SNAKEMAKE_IO_METHOD_PIPE to listOf(SECTION_OUTPUT),
SNAKEMAKE_IO_METHOD_REPEAT to listOf(SECTION_BENCHMARK),
SNAKEMAKE_IO_METHOD_UNPACK to listOf(SECTION_INPUT),
SNAKEMAKE_IO_METHOD_DYNAMIC to listOf(SECTION_OUTPUT)
)

val SMK_API_PKG_NAME_SMK = "snakemake"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,49 @@ import com.intellij.codeInspection.ProblemsHolder
import com.jetbrains.python.psi.PyArgumentList
import com.jetbrains.snakecharm.SnakemakeBundle
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SINGLE_ARGUMENT_SECTIONS_KEYWORDS
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SINGLE_ARGUMENT_WORKFLOWS_KEYWORDS
import com.jetbrains.snakecharm.lang.psi.SmkArgsSection
import com.jetbrains.snakecharm.lang.psi.SmkRuleOrCheckpointArgsSection
import com.jetbrains.snakecharm.lang.psi.SmkSubworkflowArgsSection
import com.jetbrains.snakecharm.lang.psi.SmkWorkflowArgsSection

class SmkSectionMultipleArgsInspection : SnakemakeInspection() {
override fun buildVisitor(
holder: ProblemsHolder,
isOnTheFly: Boolean,
session: LocalInspectionToolSession
holder: ProblemsHolder,
isOnTheFly: Boolean,
session: LocalInspectionToolSession,
) = object : SnakemakeInspectionVisitor(holder, session) {

override fun visitSmkSubworkflowArgsSection(st: SmkSubworkflowArgsSection) {
checkArgumentList(st.argumentList, "subworkflow")
}

override fun visitSmkRuleOrCheckpointArgsSection(st: SmkRuleOrCheckpointArgsSection) {
if (st.name in SINGLE_ARGUMENT_SECTIONS_KEYWORDS) {
checkArgumentList(st.argumentList, st.name!!)
checkArgumentList(st, SINGLE_ARGUMENT_SECTIONS_KEYWORDS)
}

override fun visitSmkWorkflowArgsSection(st: SmkWorkflowArgsSection) {
checkArgumentList(st, SINGLE_ARGUMENT_WORKFLOWS_KEYWORDS)
}

private fun checkArgumentList(st: SmkArgsSection, sectionKeywords: Set<String>) {
val keyword = st.sectionKeyword
if (keyword != null && keyword in sectionKeywords) {
checkArgumentList(st.argumentList, keyword)
}
}

private fun checkArgumentList(
argumentList: PyArgumentList?,
sectionName: String
argumentList: PyArgumentList?,
sectionName: String,
) {
val args = argumentList?.arguments ?: emptyArray()
if (args.size > 1) {
args.forEachIndexed { i, arg ->
if (i > 0) {
registerProblem(
arg,
SnakemakeBundle.message("INSP.NAME.section.multiple.args.message", sectionName)
arg,
SnakemakeBundle.message("INSP.NAME.section.multiple.args.message", sectionName)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,54 @@ import com.jetbrains.python.psi.PyArgumentList
import com.jetbrains.python.psi.PyKeywordArgument
import com.jetbrains.snakecharm.SnakemakeBundle
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.SECTIONS_WHERE_KEYWORD_ARGS_PROHIBITED
import com.jetbrains.snakecharm.codeInsight.SnakemakeAPI.WORKFLOWS_WHERE_KEYWORD_ARGS_PROHIBITED
import com.jetbrains.snakecharm.lang.psi.SmkArgsSection
import com.jetbrains.snakecharm.lang.psi.SmkRuleOrCheckpointArgsSection
import com.jetbrains.snakecharm.lang.psi.SmkSubworkflowArgsSection
import com.jetbrains.snakecharm.lang.psi.SmkWorkflowArgsSection

class SmkSectionUnexpectedKeywordArgsInspection : SnakemakeInspection() {
override fun buildVisitor(
holder: ProblemsHolder,
isOnTheFly: Boolean,
session: LocalInspectionToolSession
holder: ProblemsHolder,
isOnTheFly: Boolean,
session: LocalInspectionToolSession,
) = object : SnakemakeInspectionVisitor(holder, session) {

override fun visitSmkSubworkflowArgsSection(st: SmkSubworkflowArgsSection) {
checkArgumentList(st.argumentList, st)
}

override fun visitSmkRuleOrCheckpointArgsSection(st: SmkRuleOrCheckpointArgsSection) {
if (st.sectionKeyword in SECTIONS_WHERE_KEYWORD_ARGS_PROHIBITED) {
checkArgumentList(st, SECTIONS_WHERE_KEYWORD_ARGS_PROHIBITED)
}

override fun visitSmkWorkflowArgsSection(st: SmkWorkflowArgsSection) {
checkArgumentList(st, WORKFLOWS_WHERE_KEYWORD_ARGS_PROHIBITED)
}

private fun checkArgumentList(
st: SmkArgsSection,
sectionKeywords: Set<String>,
) {
val keyword = st.sectionKeyword
if (keyword != null && keyword in sectionKeywords) {
checkArgumentList(st.argumentList, st)
}
}

private fun checkArgumentList(
argumentList: PyArgumentList?,
section: SmkArgsSection
argumentList: PyArgumentList?,
section: SmkArgsSection,
) {
val args = argumentList?.arguments ?: emptyArray()
args.forEach { arg ->
if (arg is PyKeywordArgument) {
registerProblem(
arg,
SnakemakeBundle.message(
"INSP.NAME.section.unexpected.keyword.args.message",
section.sectionKeyword!!
)
arg,
SnakemakeBundle.message(
"INSP.NAME.section.unexpected.keyword.args.message",
section.sectionKeyword!!
)
)
}
}
Expand Down
Loading