diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index a50d4ff0290..3bad2338a27 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -1,4 +1,4 @@
-//! FIXME: write short doc here
+//! Provides set of implementation for hir's objects that allows get back location in file.
 
 use either::Either;
 use hir_def::{
diff --git a/crates/ide/src/completion.rs b/crates/ide/src/completion.rs
index 7fb4d687e1c..51dbac078f5 100644
--- a/crates/ide/src/completion.rs
+++ b/crates/ide/src/completion.rs
@@ -18,6 +18,7 @@ mod complete_unqualified_path;
 mod complete_postfix;
 mod complete_macro_in_item_position;
 mod complete_trait_impl;
+mod unstable_feature_descriptor;
 
 use ide_db::RootDatabase;
 
@@ -29,6 +30,11 @@ use crate::{
     FilePosition,
 };
 
+//FIXME: cyclic imports caused by xtask generation, this should be better
+use crate::completion::{
+    complete_attribute::LintCompletion, unstable_feature_descriptor::UNSTABLE_FEATURE_DESCRIPTOR,
+};
+
 pub use crate::completion::{
     completion_config::CompletionConfig,
     completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
diff --git a/crates/ide/src/completion/complete_attribute.rs b/crates/ide/src/completion/complete_attribute.rs
index 603d935deb7..b193c638760 100644
--- a/crates/ide/src/completion/complete_attribute.rs
+++ b/crates/ide/src/completion/complete_attribute.rs
@@ -9,6 +9,7 @@ use syntax::{ast, AstNode, SyntaxKind};
 use crate::completion::{
     completion_context::CompletionContext,
     completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
+    UNSTABLE_FEATURE_DESCRIPTOR,
 };
 
 pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
@@ -17,12 +18,15 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
         (Some(path), Some(token_tree)) if path.to_string() == "derive" => {
             complete_derive(acc, ctx, token_tree)
         }
+        (Some(path), Some(token_tree)) if path.to_string() == "feature" => {
+            complete_lint(acc, ctx, token_tree, UNSTABLE_FEATURE_DESCRIPTOR)
+        }
         (Some(path), Some(token_tree))
             if ["allow", "warn", "deny", "forbid"]
                 .iter()
                 .any(|lint_level| lint_level == &path.to_string()) =>
         {
-            complete_lint(acc, ctx, token_tree)
+            complete_lint(acc, ctx, token_tree, DEFAULT_LINT_COMPLETIONS)
         }
         (_, Some(_token_tree)) => {}
         _ => complete_attribute_start(acc, ctx, attribute),
@@ -162,9 +166,14 @@ fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input:
     }
 }
 
-fn complete_lint(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
+fn complete_lint(
+    acc: &mut Completions,
+    ctx: &CompletionContext,
+    derive_input: ast::TokenTree,
+    lints_completions: &[LintCompletion],
+) {
     if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
-        for lint_completion in DEFAULT_LINT_COMPLETIONS
+        for lint_completion in lints_completions
             .into_iter()
             .filter(|completion| !existing_lints.contains(completion.label))
         {
@@ -228,7 +237,7 @@ fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
     result
 }
 
-struct DeriveCompletion {
+pub(crate) struct DeriveCompletion {
     label: &'static str,
     dependencies: &'static [&'static str],
 }
@@ -248,9 +257,9 @@ const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
     DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
 ];
 
-struct LintCompletion {
-    label: &'static str,
-    description: &'static str,
+pub(crate) struct LintCompletion {
+    pub(crate) label: &'static str,
+    pub(crate) description: &'static str,
 }
 
 #[rustfmt::skip]
diff --git a/crates/ra_ide/src/completion/unstable_feature_descriptor.rs b/crates/ide/src/completion/unstable_feature_descriptor.rs
similarity index 100%
rename from crates/ra_ide/src/completion/unstable_feature_descriptor.rs
rename to crates/ide/src/completion/unstable_feature_descriptor.rs
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 950dd61b285..4b2b614fae6 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -29,9 +29,9 @@ pub use self::{
 // Directory used by xtask
 const STORAGE: &str = ".xtask";
 
-const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
-const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/test_data/parser/inline/ok";
-const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/test_data/parser/inline/err";
+const GRAMMAR_DIR: &str = "crates/parser/src/grammar";
+const OK_INLINE_TESTS_DIR: &str = "crates/syntax/test_data/parser/inline/ok";
+const ERR_INLINE_TESTS_DIR: &str = "crates/syntax/test_data/parser/inline/err";
 
 const SYNTAX_KINDS: &str = "crates/parser/src/syntax_kind/generated.rs";
 const AST_NODES: &str = "crates/syntax/src/ast/generated/nodes.rs";
@@ -41,7 +41,7 @@ const ASSISTS_DIR: &str = "crates/assists/src/handlers";
 const ASSISTS_TESTS: &str = "crates/assists/src/tests/generated.rs";
 
 const REPOSITORY_URL: &str = "https://github.com/rust-lang/rust";
-const UNSTABLE_FEATURE: &str = "crates/ra_ide/src/completion/unstable_feature_descriptor.rs";
+const UNSTABLE_FEATURE: &str = "crates/ide/src/completion/unstable_feature_descriptor.rs";
 const REPO_PATH: &str = "src/doc/unstable-book/src";
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
diff --git a/xtask/src/codegen/gen_unstable_future_descriptor.rs b/xtask/src/codegen/gen_unstable_future_descriptor.rs
index 3f3beb5914f..f220f85d3d6 100644
--- a/xtask/src/codegen/gen_unstable_future_descriptor.rs
+++ b/xtask/src/codegen/gen_unstable_future_descriptor.rs
@@ -15,8 +15,7 @@ fn generate_descriptor(src_dir: PathBuf) -> Result<TokenStream> {
         .filter_map(|e| e.ok())
         .filter(|entry| {
             // Get all `.md ` files
-            entry.file_type().is_file()
-                && entry.path().extension().unwrap_or_default() == "md"
+            entry.file_type().is_file() && entry.path().extension().unwrap_or_default() == "md"
         })
         .collect::<Vec<_>>();