/
CompilerFeature.kt
143 lines (124 loc) · 4.65 KB
/
CompilerFeature.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/
package org.rust.lang.core
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.psi.PsiElement
import com.intellij.util.text.SemVer
import org.rust.cargo.toolchain.RustChannel
import org.rust.ide.annotator.RsAnnotationHolder
import org.rust.ide.annotator.fixes.AddFeatureAttributeFix
import org.rust.lang.core.FeatureAvailability.*
import org.rust.lang.core.FeatureState.ACCEPTED
import org.rust.lang.core.psi.ext.*
import org.rust.lang.core.stubs.index.RsFeatureIndex
import org.rust.lang.utils.RsDiagnostic
import org.rust.lang.utils.addToHolder
import org.rust.lang.utils.evaluation.CfgEvaluator
class CompilerFeature(
val name: String,
val state: FeatureState,
val since: SemVer?,
cache: Boolean = true
) {
constructor(
name: String,
state: FeatureState,
since: String,
cache: Boolean = true
) : this(name, state, SemVer.parseFromText(since)!!, cache)
init {
if (cache) {
knownFeatures[name] = this
}
}
fun availability(element: PsiElement): FeatureAvailability {
val rsElement = element.ancestorOrSelf<RsElement>() ?: return UNKNOWN
val version = rsElement.cargoProject?.rustcInfo?.version ?: return UNKNOWN
if (state == ACCEPTED && (since == null || version.semver >= since)) return AVAILABLE
if (version.channel != RustChannel.NIGHTLY) return NOT_AVAILABLE
val crate = rsElement.containingCrate ?: return UNKNOWN
val cfgEvaluator = CfgEvaluator.forCrate(crate)
val attrs = RsFeatureIndex.getFeatureAttributes(element.project, name)
val possibleFeatureAttrs = attrs.asSequence()
.filter { it.containingCrate == crate }
.flatMap { cfgEvaluator.expandCfgAttrs(sequenceOf(it.metaItem)) }
for (featureAttr in possibleFeatureAttrs) {
if (featureAttr.name != "feature") continue
val metaItems = featureAttr.metaItemArgs?.metaItemList.orEmpty()
if (metaItems.any { feature -> feature.name == name }) return AVAILABLE
}
return CAN_BE_ADDED
}
fun check(
holder: RsAnnotationHolder,
element: PsiElement,
presentableFeatureName: String,
vararg fixes: LocalQuickFix
) = check(holder, element, null, "$presentableFeatureName is experimental", *fixes)
fun check(
holder: AnnotationHolder,
element: PsiElement,
presentableFeatureName: String,
vararg fixes: LocalQuickFix
) = check(holder, element, null, "$presentableFeatureName is experimental", *fixes)
fun check(
holder: AnnotationHolder,
startElement: PsiElement,
endElement: PsiElement?,
message: String,
vararg fixes: LocalQuickFix
) {
getDiagnostic(startElement, endElement, message, *fixes)?.addToHolder(holder)
}
fun check(
holder: RsAnnotationHolder,
startElement: PsiElement,
endElement: PsiElement?,
message: String,
vararg fixes: LocalQuickFix
) {
getDiagnostic(startElement, endElement, message, *fixes)?.addToHolder(holder)
}
fun addFeatureFix(element: PsiElement) =
AddFeatureAttributeFix(name, element)
private fun getDiagnostic(
startElement: PsiElement,
endElement: PsiElement?,
message: String,
vararg fixes: LocalQuickFix
) = when (availability(startElement)) {
NOT_AVAILABLE -> RsDiagnostic.ExperimentalFeature(startElement, endElement, message, fixes.toList())
CAN_BE_ADDED -> {
val fix = addFeatureFix(startElement)
RsDiagnostic.ExperimentalFeature(startElement, endElement, message, listOf(*fixes, fix))
}
else -> null
}
companion object {
private val knownFeatures: MutableMap<String, CompilerFeature> = hashMapOf()
fun find(featureName: String): CompilerFeature? = knownFeatures[featureName]
}
}
enum class FeatureState {
/**
* Represents active features that are currently being implemented or
* currently being considered for addition/removal.
* Such features can be used only with nightly compiler with the corresponding feature attribute
*/
ACTIVE,
/**
* Those language feature has since been Accepted (it was once Active)
* so such language features can be used with stable/beta compiler since some version
* without any additional attributes
*/
ACCEPTED
}
enum class FeatureAvailability {
AVAILABLE,
CAN_BE_ADDED,
NOT_AVAILABLE,
UNKNOWN
}