Repository: intellij-rust/intellij-rust
Issue: `Extract Trait` refactoring doesn't add `pub`

## Environment

* **IntelliJ Rust plugin version:** 0.4.165.4391-213-nightly
* **Rust toolchain version:** 1.59.0-nightly (0b42deacc 2021-12-09) x86_64-apple-darwin
* **IDE name and version:** IntelliJ IDEA 2021.3.1 Ultimate Edition (IU-213.6461.79)
* **Operating system:** macOS 11.5.1
* **Macro expansion engine:** new
* **Name resolution engine:** new
* **Additional experimental features:** org.rust.cargo.features.settings.gutter

## Problem description
`Extract Trait` refactoring doesn't add trait imports for usages and doesn't add `pub`

## Steps to reproduce

- There is a code

```rust
pub mod color {
    pub struct Color(pub u8, pub u8, pub u8);
}

pub mod values {
    use super::color::Color;
    impl Color {
        /*caret*/ pub fn red() -> Color {
            Color(255, 0, 0)
        }
    }
}

pub use self::color::Color;
fn main() {
    color::Color::red();
}
```
- Call `Refactor -> Extract / Introduce -> Trait`

## Expected Results
```rust
pub mod color {
    pub struct Color(pub u8, pub u8, pub u8);
}

pub mod values {
    use super::color::Color;

    impl ExtractedTrait for Color {
        fn red() -> Color {
            Color(255, 0, 0)
        }
    }

    pub trait ExtractedTrait {
        fn red() -> Color;
    }
}

use crate::values::ExtractedTrait;
pub use self::color::Color;
fn main() {
    color::Color::red();
}
```

## Actual Results
There is no import for usages
```rust
pub mod color {
    pub struct Color(pub u8, pub u8, pub u8);
}

pub mod values {
    use super::color::Color;

    impl ExtractedTrait for Color {
        fn red() -> Color {
            Color(255, 0, 0)
        }
    }

    trait ExtractedTrait {
        fn red() -> Color;
    }
}

pub use self::color::Color;
fn main() {
    color::Color::red();
}
```

Changed files:
src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitFromImplTest.kt
src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractSuperTraitTest.kt
src/main/kotlin/org/rust/lang/core/psi/ext/RsVisibility.kt
src/main/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitProcessor.kt

diff --git a/src/main/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitProcessor.kt b/src/main/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitProcessor.kt
index d21181d1d..9ba8dc40f 100644
--- a/src/main/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitProcessor.kt
+++ b/src/main/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitProcessor.kt
@@ -131,8 +131,9 @@ class RsExtractTraitProcessor(
         }
         val traitBody = members.joinToString(separator = "\\n") { it.text }
 
+        val visibility = getTraitVisibility()
         val trait = psiFactory.tryCreateTraitItem(
-            "trait $traitName $typeParameters $whereClause {\\n$traitBody\\n}"
+            "${visibility}trait $traitName $typeParameters $whereClause {\\n$traitBody\\n}"
         ) ?: return null
 
         copyAttributes(traitOrImpl, trait)
@@ -155,6 +156,15 @@ class RsExtractTraitProcessor(
         return impl.parent.addAfter(newImpl, impl) as? RsImplItem
     }
 
+    private fun getTraitVisibility(): String {
+        val unitedVisibility = when (traitOrImpl) {
+            is RsTraitItem -> traitOrImpl.visibility
+            is RsImplItem -> members.map { it.visibility }.reduce(RsVisibility::unite)
+            else -> error("unreachable")
+        }
+        return unitedVisibility.format()
+    }
+
     private fun extractMembersFromOldImpl(impl: RsImplItem): String {
         val membersNames = members.mapNotNullToSet { it.name }
         val membersToMove = impl.getMembersWithNames(membersNames)
diff --git a/src/main/kotlin/org/rust/lang/core/psi/ext/RsVisibility.kt b/src/main/kotlin/org/rust/lang/core/psi/ext/RsVisibility.kt
index 8737065da..34bc0de61 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/ext/RsVisibility.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/ext/RsVisibility.kt
@@ -96,6 +96,30 @@ fun RsVisibility.intersect(other: RsVisibility): RsVisibility = when (this) {
     }
 }
 
+fun RsVisibility.unite(other: RsVisibility): RsVisibility = when {
+    this is RsVisibility.Restricted && other is RsVisibility.Restricted -> {
+        val commonParent = commonParentMod(inMod, other.inMod)
+        if (commonParent != null) {
+            RsVisibility.Restricted(commonParent)
+        } else {
+            RsVisibility.Public
+        }
+    }
+    this == RsVisibility.Private && other is RsVisibility.Private -> RsVisibility.Private
+    else -> RsVisibility.Public
+}
+
+fun RsVisibility.format(): String = when (this) {
+    RsVisibility.Private -> ""
+    RsVisibility.Public -> "pub "
+    is RsVisibility.Restricted ->
+        if (inMod.isCrateRoot) {
+            "pub(crate) "
+        } else {
+            "pub(in crate${inMod.crateRelativePath}) "
+        }
+}
+
 val RsVis.visibility: RsVisibility
     get() = when (stubKind) {
         RsVisStubKind.PUB -> RsVisibility.Public
diff --git a/src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractSuperTraitTest.kt b/src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractSuperTraitTest.kt
index 49712ba50..5f9639c21 100644
--- a/src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractSuperTraitTest.kt
+++ b/src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractSuperTraitTest.kt
@@ -48,6 +48,50 @@ class RsExtractSuperTraitTest : RsExtractTraitBaseTest() {
         }
     """)
 
+    fun `test public trait`() = doTest("""
+        pub trait Derived {
+            /*caret*/fn a(&self);
+        }
+    """, """
+        pub trait Derived: Trait {}
+
+        pub trait Trait {
+            fn a(&self);
+        }
+    """)
+
+    fun `test restricted trait 1`() = doTest("""
+        pub(crate) trait Derived {
+            /*caret*/fn a(&self);
+        }
+    """, """
+        pub(crate) trait Derived: Trait {}
+
+        pub(crate) trait Trait {
+            fn a(&self);
+        }
+    """)
+
+    fun `test restricted trait 2`() = doTest("""
+        mod inner1 {
+            mod inner2 {
+                pub(super) trait Derived {
+                    /*caret*/fn a(&self);
+                }
+            }
+        }
+    """, """
+        mod inner1 {
+            mod inner2 {
+                pub(super) trait Derived: Trait {}
+
+                pub(in crate::inner1) trait Trait {
+                    fn a(&self);
+                }
+            }
+        }
+    """)
+
     fun `test impl in same mod`() = doTest("""
         trait Derived {
             /*caret*/fn a(&self);
diff --git a/src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitFromImplTest.kt b/src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitFromImplTest.kt
index fad1c2e1a..af720b343 100644
--- a/src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitFromImplTest.kt
+++ b/src/test/kotlin/org/rust/ide/refactoring/extractTrait/RsExtractTraitFromImplTest.kt
@@ -77,11 +77,74 @@ class RsExtractTraitTest : RsExtractTraitBaseTest() {
             fn a(&self) {}
         }
 
-        trait Trait {
+        pub trait Trait {
             fn a(&self);
         }
     """)
 
+    fun `test public method with usage`() = doTest("""
+        mod inner {
+            pub struct S;
+            impl S {
+                /*caret*/pub fn a(&self) {}
+            }
+        }
+
+        use crate::inner::S;
+        fn foo(s: S) {
+            s.a();
+        }
+    """, """
+        mod inner {
+            pub struct S;
+
+            impl Trait for S {
+                fn a(&self) {}
+            }
+
+            pub trait Trait {
+                fn a(&self);
+            }
+        }
+
+        use crate::inner::{S, Trait};
+        fn foo(s: S) {
+            s.a();
+        }
+    """)
+
+    fun `test restricted methods`() = doTest("""
+        mod mod1 {
+            mod mod2 {
+                mod mod3 {
+                    pub struct S;
+                    impl S {
+                        /*caret*/pub(in crate::mod1) fn a(&self) {}
+                        /*caret*/pub(in crate::mod1::mod2) fn b(&self) {}
+                    }
+                }
+            }
+        }
+    """, """
+        mod mod1 {
+            mod mod2 {
+                mod mod3 {
+                    pub struct S;
+
+                    impl Trait for S {
+                        fn a(&self) {}
+                        fn b(&self) {}
+                    }
+
+                    pub(in crate::mod1) trait Trait {
+                        fn a(&self);
+                        fn b(&self);
+                    }
+                }
+            }
+        }
+    """)
+
     fun `test associated constant`() = doTest("""
         struct S;
         impl S {