Skip to content

Commit

Permalink
Merge #4335
Browse files Browse the repository at this point in the history
4335: RES: take into account cfg attributes when collecting impls r=vlad20012 a=vlad20012

See #4232

Co-authored-by: vlad20012 <beskvlad@gmail.com>
  • Loading branch information
bors[bot] and vlad20012 committed Sep 9, 2019
2 parents fd943a8 + f61dbaa commit 413fd6e
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 10 deletions.
32 changes: 30 additions & 2 deletions src/main/kotlin/org/rust/lang/core/psi/RsFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import org.rust.lang.RsConstants
import org.rust.lang.RsFileType
import org.rust.lang.RsLanguage
import org.rust.lang.core.completion.getOriginalOrSelf
import org.rust.lang.core.macros.RsExpandedElement
import org.rust.lang.core.macros.expandedFrom
import org.rust.lang.core.macros.macroExpansionManager
import org.rust.lang.core.psi.ext.*
import org.rust.lang.core.resolve.ref.RsReference
Expand Down Expand Up @@ -68,6 +70,7 @@ class RsFile(
val cargoWorkspace: CargoWorkspace? get() = cachedData.cargoWorkspace
val cargoTarget: CargoWorkspace.Target? get() = cachedData.cargoTarget
override val crateRoot: RsMod? get() = cachedData.crateRoot
val isDeeplyEnabledByCfg: Boolean get() = cachedData.isDeeplyEnabledByCfg

private val cachedData: CachedData
get() {
Expand All @@ -85,7 +88,10 @@ class RsFile(
check(originalFile == this)

val declaration = declaration
if (declaration != null) return (declaration.contextualFile as? RsFile)?.cachedData ?: EMPTY_CACHED_DATA
if (declaration != null) {
val (file, isEnabledByCfg) = declaration.contextualFileAndIsEnabledByCfgOnThisWay()
return (file as? RsFile)?.cachedData?.copy(isDeeplyEnabledByCfg = isEnabledByCfg) ?: EMPTY_CACHED_DATA
}

val possibleCrateRoot = this

Expand Down Expand Up @@ -188,7 +194,8 @@ private data class CachedData(
* May be not equal to [cargoTarget]'s [CargoWorkspace.Target.crateRoot].
* For example, in the case of doctest injection
*/
val crateRoot: RsFile? = null
val crateRoot: RsFile? = null,
val isDeeplyEnabledByCfg: Boolean = true
)

private val EMPTY_CACHED_DATA: CachedData = CachedData()
Expand All @@ -208,3 +215,24 @@ private val MOD_DECL_MACROS_KEY: Key<CachedValue<RsModDeclItem?>> = Key.create("

private val CACHED_DATA_KEY: Key<CachedValue<CachedData>> = Key.create("CACHED_DATA_KEY")
private val CACHED_DATA_MACROS_KEY: Key<CachedValue<CachedData>> = Key.create("CACHED_DATA_MACROS_KEY")

private tailrec fun PsiElement.contextualFileAndIsEnabledByCfgOnThisWay(): Pair<PsiFile, Boolean> {
if (this is RsDocAndAttributeOwner && !isEnabledByCfg) return contextualFile to false
val contextOrMacro = (this as? RsExpandedElement)?.expandedFrom ?: context!!
return if (contextOrMacro is PsiFile) {
contextOrMacro to (contextOrMacro !is RsDocAndAttributeOwner || contextOrMacro.isEnabledByCfg)
} else {
contextOrMacro.contextualFileAndIsEnabledByCfgOnThisWay()
}
}

/**
* @return true if containing crate root is known for this element and this element is not excluded from
* a project via `#[cfg]` attribute on some level (e.g. its parent module)
*/
val RsElement.isValidProjectMember: Boolean
get() {
val (file, isEnabledByCfg) = contextualFileAndIsEnabledByCfgOnThisWay()
if (file !is RsFile) return true
return isEnabledByCfg && file.isDeeplyEnabledByCfg && file.crateRoot != null
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import org.rust.lang.core.psi.RsImplItem
import org.rust.lang.core.psi.RsTraitItem
import org.rust.lang.core.psi.RsTraitRef
import org.rust.lang.core.psi.ext.RsAbstractable
import org.rust.lang.core.psi.ext.RsMod
import org.rust.lang.core.psi.ext.expandedMembers
import org.rust.lang.core.psi.ext.resolveToBoundTrait
import org.rust.lang.core.psi.isValidProjectMember
import org.rust.lang.core.resolve.ref.ResolveCacheDependency
import org.rust.lang.core.resolve.ref.RsResolveCache
import org.rust.lang.core.types.BoundElement
Expand All @@ -28,10 +28,11 @@ import kotlin.LazyThreadSafetyMode.PUBLICATION
* [ImplLookup.assembleCandidates] and [processAssociatedItems] in particular
*/
class RsCachedImplItem(
val impl: RsImplItem,
val crateRoot: RsMod? = impl.crateRoot,
val traitRef: RsTraitRef? = impl.traitRef
val impl: RsImplItem
) {
val traitRef: RsTraitRef? = impl.traitRef
val isValid: Boolean = impl.isValidProjectMember

val implementedTrait: BoundElement<RsTraitItem>? by lazy(PUBLICATION) { traitRef?.resolveToBoundTrait() }
val typeAndGenerics: Pair<Ty, List<TyTypeParameter>>? by lazy(PUBLICATION) {
impl.typeReference?.type?.let { it to impl.generics }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class RsImplIndex : AbstractStubIndex<TyFingerprint, RsImplItem>() {
// filter dangling (not attached to some crate) rust files, e.g. tests, generated source
return (impls.asSequence() + freeImpls.asSequence())
.map { RsCachedImplItem.forImpl(project, it) }
.filter { it.crateRoot != null }
.filter { it.isValid }
}

/** return impls for generic type `impl<T> Trait for T {}` */
Expand All @@ -52,7 +52,7 @@ class RsImplIndex : AbstractStubIndex<TyFingerprint, RsImplItem>() {
// filter dangling (not attached to some crate) rust files, e.g. tests, generated source
return freeImpls.asSequence()
.map { RsCachedImplItem.forImpl(project, it) }
.filter { it.crateRoot != null }
.filter { it.isValid }
}

fun index(stub: RsImplItemStub, sink: IndexSink) {
Expand Down
96 changes: 94 additions & 2 deletions src/test/kotlin/org/rust/lang/core/resolve/RsCfgAttrResolveTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import org.rust.MockAdditionalCfgOptions
import org.rust.lang.core.psi.RsTupleFieldDecl

class RsCfgAttrResolveTest : RsResolveTestBase() {
override val followMacroExpansions: Boolean get() = true

@MockAdditionalCfgOptions("foo")
fun `test fn with cfg`() = checkByCode("""
#[cfg(foo)]
Expand Down Expand Up @@ -127,16 +129,106 @@ class RsCfgAttrResolveTest : RsResolveTestBase() {

@MockAdditionalCfgOptions("foo")
fun `test extern crate with cfg`() = stubOnlyResolve("""
//- main.rs
//- main.rs
#[cfg(foo)]
extern crate test_package;
fn main() {
test_package::hello();
} //^ lib.rs
//- lib.rs
//- lib.rs
pub fn hello() {}
//X
""")

@MockAdditionalCfgOptions("foo")
fun `test impl`() = checkByCode("""
struct S;
#[cfg(foo)]
impl S { fn foo(&self) {} }
//X
#[cfg(not(foo))]
impl S { fn foo(&self) {} }
fn main() {
S.foo()
} //^
""")

@MockAdditionalCfgOptions("foo")
fun `test impl in inline mod with cfg`() = checkByCode("""
struct S;
#[cfg(foo)]
mod foo {
impl super::S { fn foo(&self) {} }
//X
}
#[cfg(not(foo))]
mod foo {
impl super::S { fn foo(&self) {} }
}
fn main() {
S.foo()
} //^
""")

@MockAdditionalCfgOptions("foo")
fun `test impl in non-inline mod with cfg`() = stubOnlyResolve("""
//- bar.rs
impl super::S { fn foo(&self) {} }
//- baz.rs
impl super::S { fn foo(&self) {} }
//- lib.rs
struct S;
#[cfg(foo)]
#[path = "bar.rs"]
mod foo;
#[cfg(not(foo))]
#[path = "baz.rs"]
mod foo;
fn main() {
S.foo()
} //^ bar.rs
""")

@MockAdditionalCfgOptions("foo")
fun `test impl in function body with cfg`() = checkByCode("""
struct S;
#[cfg(foo)]
fn foo() {
impl S { fn foo(&self) {} }
//X
}
#[cfg(not(foo))]
fn foo() {
impl S { fn foo(&self) {} }
}
fn main() {
S.foo()
} //^
""")

@ExpandMacros
@MockAdditionalCfgOptions("foo")
fun `test impl expanded from macro with cfg`() = checkByCode("""
macro_rules! same {
($ i:item) => { $ i };
}
struct S;
#[cfg(foo)]
same! {
impl S { fn foo(&self) {} }
//X
}
#[cfg(not(foo))]
same! {
impl S { fn foo(&self) {} }
}
fn main() {
S.foo()
} //^
""")
}

0 comments on commit 413fd6e

Please sign in to comment.