Remove lints and associated code from 'rocket_codegen'.

Rust's linting API is incredibly unstable, resulting in unnecessary
breakage to `rocket_codegen`. Rocket's lints are also not as
conservative as would be desired, resulting in spurious warnings. For
these reasons, this commit removes linting from `rocket_codegen`.

These lints will likely be reintroduced as part of a 'rocket_lints'
crate. Factoring the lints out to a separate crate means that lint
breakage can be dealt with by uncommenting the dependency instead of
waiting for a new release or backtracking nightlies. In the same vein,
it will likely improve stability of the 'rocket_codegen' crate.
This commit is contained in:
Sergio Benitez 2017-08-15 11:39:22 -07:00
parent f66780bd9e
commit 8183f63630
10 changed files with 4 additions and 646 deletions

View File

@ -1,10 +1,9 @@
//! # Rocket - Code Generation
//!
//! This crate implements the code generation portions of Rocket. This includes
//! custom derives, custom attributes, procedural macros, and lints. The
//! documentation here is purely technical. The code generation facilities are
//! documented thoroughly in the [Rocket programming
//! guide](https://rocket.rs/guide).
//! custom derives, custom attributes, and procedural macros. The documentation
//! here is purely technical. The code generation facilities are documented
//! thoroughly in the [Rocket programming guide](https://rocket.rs/guide).
//!
//! ## Custom Attributes
//!
@ -126,19 +125,6 @@
//! PATH := a path, as defined by Rust
//! </pre>
//!
//! ## Lints
//!
//! This crate implements the following lints:
//!
//! * **unmounted_route**: defaults to _warn_
//!
//! emits a warning when a declared route is not mounted
//!
//! * **unmanaged_state**: defaults to _warn_
//!
//! emits a warning when a `State<T>` request guest is used in a mounted
//! route without managing a value for `T`
//!
//! # Debugging Codegen
//!
//! When the `ROCKET_CODEGEN_DEBUG` environment variable is set, this crate logs
@ -158,7 +144,7 @@
#![allow(deprecated)]
#[macro_use] extern crate log;
#[macro_use] extern crate rustc;
extern crate rustc;
extern crate syntax;
extern crate syntax_ext;
extern crate syntax_pos;
@ -169,7 +155,6 @@ extern crate rocket;
mod parser;
mod macros;
mod decorators;
mod lints;
use std::env;
use rustc_plugin::Registry;
@ -205,12 +190,6 @@ macro_rules! register_derives {
)
}
macro_rules! register_lints {
($registry:expr, $($item:ident),+) => ($(
$registry.register_late_lint_pass(Box::new(lints::$item::default()));
)+)
}
/// Compiler hook for Rust to register plugins.
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
@ -237,6 +216,4 @@ pub fn plugin_registrar(reg: &mut Registry) {
"patch" => patch_decorator,
"options" => options_decorator
);
register_lints!(reg, RocketLint);
}

View File

@ -1,337 +0,0 @@
mod utils;
use self::utils::*;
use ::{ROUTE_ATTR, ROUTE_INFO_ATTR};
use std::mem::transmute;
use std::collections::HashMap;
use rustc::lint::{Level, LateContext, LintPass, LateLintPass, LintArray};
use rustc::hir::{Item, Expr, Crate, Decl, FnDecl, Body, QPath, PatKind};
use rustc::hir::def_id::DefId;
use rustc::ty::Ty;
use rustc::hir::intravisit::{FnKind};
use rustc::hir::Decl_::*;
use rustc::hir::Expr_::*;
use syntax_pos::Span;
use syntax::symbol::Symbol as Name;
use syntax::ast::NodeId;
const STATE_TYPE: &'static [&'static str] = &["rocket", "request", "state", "State"];
// Information about a specific Rocket instance.
#[derive(Debug, Default)]
struct InstanceInfo {
// Mapping from mounted struct info to the span of the mounted call.
mounted: HashMap<DefId, Span>,
// Mapping from managed types to the span of the manage call.
managed: HashMap<Ty<'static>, Span>,
}
/// A `Receiver` captures the "receiver" of a Rocket instance method call. A
/// Receiver can be an existing instance of Rocket or a call to an Rocket
/// initialization function.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
enum Receiver {
Instance(DefId, Span),
Call(NodeId, Span),
Relative(Span),
}
impl Receiver {
/// Returns the span associated with the receiver.
pub fn span(&self) -> Span {
use self::Receiver::*;
match *self {
Instance(_, sp) | Call(_, sp) | Relative(sp) => sp
}
}
}
#[derive(Debug, Default)]
pub struct RocketLint {
// All of the types that were requested as managed state.
// (fn_name, fn_span, info_struct_def_id, req_type, req_param_span)
requested: Vec<(Name, Span, DefId, Ty<'static>, Span)>,
// Mapping from a `Rocket` instance initialization call span (an ignite or
// custom call) to the collected info about that instance.
instances: HashMap<Option<Receiver>, InstanceInfo>,
// Map of all route info structure names found in the program to its defid.
// This is used to map a declared route to its info structure defid.
info_structs: HashMap<Name, DefId>,
// The name, span, and info DefId for all route functions found. The DefId
// is obtained by indexing into info_structs with the name found in the
// attribute that Rocket generates.
declared: Vec<(Name, Span, DefId)>,
// Mapping from known named Rocket instances to initial receiver. This is
// used to do a sort-of flow-based analysis. We track variable declarations
// and link calls to Rocket methods to the (as best as we can tell) initial
// call to generate that Rocket instance. We use this to group calls to
// `manage` and `mount` to give more accurate warnings.
instance_vars: HashMap<DefId, Receiver>,
}
declare_lint!(UNMOUNTED_ROUTE, Warn, "Warn on routes that are unmounted.");
declare_lint!(UNMANAGED_STATE, Warn, "Warn on declared use of unmanaged state.");
impl<'tcx> LintPass for RocketLint {
fn get_lints(&self) -> LintArray {
lint_array!(UNMANAGED_STATE, UNMOUNTED_ROUTE)
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RocketLint {
// This fills out the `instance_vars` table by tracking calls to
// function/methods that create Rocket instances. If the call is a method
// call with a receiver that we know is a Rocket instance, then we know it's
// been moved, and we track that move by linking all definition to the same
// receiver.
fn check_decl(&mut self, cx: &LateContext<'a, 'tcx>, decl: &'tcx Decl) {
// We only care about local declarations...everything else seems very
// unlikely. This is imperfect, after all.
if let DeclLocal(ref local) = decl.node {
// Retrieve the def_id for the new binding.
let new_def_id = match local.pat.node {
PatKind::Binding(_, def_id, ..) => def_id ,
_ => return
};
// `init` is the RHS of the declaration.
if let Some(ref init) = local.init {
// We only care about declarations that result in Rocket insts.
if !returns_rocket_instance(cx, init) {
return;
}
let (expr, span) = match find_initial_receiver(cx, init) {
Some(expr) => (expr, expr.span),
None => return
};
// If the receiver is a path, check if this path was declared
// before by another binding and use that binding's receiver as
// this binding's receiver, essentially taking us back in time.
// If we don't know about it, just insert a new receiver.
if let ExprPath(QPath::Resolved(_, ref path)) = expr.node {
if let Some(old_def_id) = path.def.def_id_opt() {
if let Some(&prev) = self.instance_vars.get(&old_def_id) {
self.instance_vars.insert(new_def_id, prev);
} else {
let recvr = Receiver::Instance(old_def_id, span);
self.instance_vars.insert(new_def_id, recvr);
}
}
}
// We use a call as a base case. Maybe it's a brand new Rocket
// instance, maybe it's a function returning a Rocket instance.
// Who knows. This is where imperfection comes in. We're just
// going to assume that calls to `mount` and `manage` are
// grouped with their originating call.
if let ExprCall(ref expr, ..) = expr.node {
let recvr = Receiver::Call(expr.id, span);
self.instance_vars.insert(new_def_id, recvr);
}
}
}
}
// Here, we collect all of the calls to `manage` and `mount` by instance,
// where the instance is determined by the receiver of the call. We look up
// the receiver in the type table we've constructed. If it's there, we use
// it, if not, we use the call as the receiver.
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
// Fetches the top-level `Receiver` instance given that a method call
// was made to the receiver `rexpr`. Top-level here means "the
// original". We search the `instance_vars` table to retrieve it.
let instance_for = |lint: &mut RocketLint, rexpr: &Expr| -> Option<Receiver> {
match rexpr.node {
ExprPath(QPath::Resolved(_, ref p)) => {
p.def.def_id_opt()
.and_then(|id| lint.instance_vars.get(&id))
.map(|recvr| recvr.clone())
}
ExprPath(QPath::TypeRelative(_, _)) => {
Some(Receiver::Relative(rexpr.span))
}
ExprCall(ref c, ..) => Some(Receiver::Call(c.id, rexpr.span)),
_ => panic!("Unexpected node: {:?}", rexpr.node)
}
};
if let Some((recvr, args)) = rocket_method_call("manage", cx, expr) {
let managed_val = &args[0];
let instance = recvr.and_then(|r| instance_for(self, r));
if let Some(managed_ty) = cx.tables.expr_ty_opt(managed_val) {
self.instances.entry(instance)
.or_insert_with(|| InstanceInfo::default())
.managed
.insert(unsafe { transmute(managed_ty) }, managed_val.span);
}
}
if let Some((recvr, args)) = rocket_method_call("mount", cx, expr) {
let instance = recvr.and_then(|r| instance_for(self, r));
for def_id in extract_mount_fn_def_ids(cx, &args[1]) {
self.instances.entry(instance)
.or_insert_with(|| InstanceInfo::default())
.mounted
.insert(def_id, expr.span);
}
}
// This captures a corner case where neither `manage` nor `mount` is
// called on an instance.
if let Some((Some(recvr_expr), _)) = rocket_method_call("launch", cx, expr) {
if let Some(instance) = instance_for(self, recvr_expr) {
self.instances.entry(Some(instance))
.or_insert_with(|| InstanceInfo::default());
}
}
}
// We collect all of the names and defids for the info structures that
// Rocket has generated. We do this by simply looking at the attribute,
// which Rocket's codegen was kind enough to generate.
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
// Return early if this is not a route info structure.
if !item.attrs.iter().any(|attr| attr.check_name(ROUTE_INFO_ATTR)) {
return;
}
if let Some(def_id) = cx.tcx.hir.opt_local_def_id(item.id) {
self.info_structs.insert(item.name, def_id);
}
}
/// We do two things here: 1) we find all of the `State` request guards a
/// user wants, and 2) we find all of the routes declared by the user. We
/// determine that a function is a route by looking for the attribute that
/// Rocket declared. We tie the route to the info structure, obtained from
/// the `check_item` call, so that we can determine if the route was mounted
/// or not. The tie is done by looking at the name of the info structure in
/// the attribute that Rocket generated and then looking up the structure in
/// the `info_structs` map. The structure _must_ be there since Rocket
/// always generates the structure before the route.
fn check_fn(&mut self,
cx: &LateContext<'a, 'tcx>,
kind: FnKind<'tcx>,
decl: &'tcx FnDecl,
_: &'tcx Body,
fn_sp: Span,
fn_id: NodeId) {
// Get the name of the function, if any.
let fn_name = match kind {
FnKind::ItemFn(name, ..) => name,
_ => return
};
// Figure out if this is a route function by trying to find the
// `ROUTE_ATTR` attribute and extracing the info struct's name from it.
let attr_value = kind.attrs().iter().filter_map(|attr| {
match attr.check_name(ROUTE_ATTR) {
false => None,
true => attr.meta_item_list().and_then(|list| list[0].name())
}
}).next();
// Try to get the DEF_ID using the info struct's name from the
// `info_structs` map. Return early if anything goes awry.
let def_id = match attr_value {
Some(val) if self.info_structs.contains_key(&val) => {
self.info_structs.get(&val).unwrap()
}
_ => return
};
// Add this to the list of declared routes to check for mounting later
// unless unmounted routes were explicitly allowed for this function.
if cx.tcx.lint_level_at_node(UNMOUNTED_ROUTE, fn_id).0 != Level::Allow {
self.declared.push((fn_name, fn_sp, def_id.clone()));
}
// If unmanaged state was explicitly allowed for this function, don't
// record any additional information. Just return now.
if cx.tcx.lint_level_at_node(UNMANAGED_STATE, fn_id).0 == Level::Allow {
return;
}
// Collect all of the `State` types and spans into `tys` and `spans`.
let mut ty_and_spans: Vec<(Ty<'static>, Span)> = vec![];
if let Some(sig) = cx.tables.liberated_fn_sigs.get(&fn_id) {
for (i, input_ty) in sig.inputs().iter().enumerate() {
let def_id = match input_ty.ty_to_def_id() {
Some(id) => id,
None => continue
};
if match_def_path(cx.tcx, def_id, STATE_TYPE) {
if let Some(inner_type) = input_ty.walk_shallow().next() {
if decl.inputs.len() <= i {
println!("internal lint error: \
signature and declaration length mismatch: \
{:?}, {:?}", sig.inputs(), decl.inputs);
println!("this is likely a bug. please report this.");
continue;
}
let ty = unsafe { transmute(inner_type) };
let span = decl.inputs[i].span;
ty_and_spans.push((ty, span));
}
}
}
}
// Insert the information we've collected.
for (ty, span) in ty_and_spans {
self.requested.push((fn_name, fn_sp, def_id.clone(), ty, span));
}
}
fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate) {
// Iterate through all the instances, emitting warnings.
for (instance, info) in self.instances.iter() {
self.unmounted_warnings(cx, *instance, info);
self.unmanaged_warnings(cx, *instance, info);
}
}
}
impl RocketLint {
fn unmounted_warnings(&self, cx: &LateContext,
rcvr: Option<Receiver>,
info: &InstanceInfo) {
// Emit a warning for all unmounted, declared routes.
for &(route_name, fn_sp, info_def_id) in self.declared.iter() {
if !info.mounted.contains_key(&info_def_id) {
let help_span = rcvr.map(|r| r.span());
msg_and_help(cx, UNMOUNTED_ROUTE, fn_sp,
&format!("the '{}' route is not mounted", route_name),
"Rocket will not dispatch requests to unmounted routes.",
help_span, "maybe add a call to `mount` here?");
}
}
}
fn unmanaged_warnings(&self,
cx: &LateContext,
rcvr: Option<Receiver>,
info: &InstanceInfo) {
for &(_, _, info_def_id, ty, sp) in self.requested.iter() {
// Don't warn on unmounted routes.
if !info.mounted.contains_key(&info_def_id) { continue }
if !info.managed.contains_key(&ty) {
let help_span = rcvr.map(|r| r.span());
msg_and_help(cx, UNMANAGED_STATE, sp,
&format!("'{}' is not currently being managed by Rocket", ty),
"this 'State' request guard will always fail",
help_span, "maybe add a call to `manage` here?");
}
}
}
}

View File

@ -1,177 +0,0 @@
use rustc::ty;
use rustc::hir::def_id::DefId;
use rustc::lint::{LintContext, Lint, LateContext};
use rustc::hir::Expr_::*;
use rustc::hir::Expr;
use rustc::hir::def::Def;
use syntax::symbol;
use syntax_pos::Span;
const ROCKET_TYPE: &'static [&'static str] = &["rocket", "rocket", "Rocket"];
const ROCKET_IGNITE_FN: &'static [&'static str] = &["rocket", "ignite"];
const ROCKET_IGNITE_STATIC: &'static [&'static str] = &["rocket", "rocket",
"Rocket", "ignite"];
const ROCKET_CUSTOM_FN: &'static [&'static str] = &["rocket", "custom"];
const ROCKET_CUSTOM_STATIC: &'static [&'static str] = &["rocket", "rocket",
"Rocket", "custom"];
const ABSOLUTE: &'static ty::item_path::RootMode =
&ty::item_path::RootMode::Absolute;
/// Check if a `DefId`'s path matches the given absolute type path usage.
pub fn match_def_path(tcx: ty::TyCtxt, def_id: DefId, path: &[&str]) -> bool {
struct AbsolutePathBuffer {
names: Vec<symbol::InternedString>,
}
impl ty::item_path::ItemPathBuffer for AbsolutePathBuffer {
fn root_mode(&self) -> &ty::item_path::RootMode {
ABSOLUTE
}
fn push(&mut self, text: &str) {
self.names.push(symbol::Symbol::intern(text).as_str());
}
}
let mut apb = AbsolutePathBuffer { names: vec![] };
tcx.push_item_path(&mut apb, def_id);
apb.names.len() == path.len() &&
apb.names.iter().zip(path.iter()).all(|(a, &b)| &**a == b)
}
/// Check if the method call given in `expr` belongs to given type.
pub fn is_impl_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool {
cx.tables.type_dependent_defs
.get(&expr.id)
.and_then(|callee| cx.tcx.impl_of_method(callee.def_id()))
.map(|trt_id| match_def_path(cx.tcx, trt_id, path))
.unwrap_or(false)
}
pub fn find_initial_receiver<'e>(cx: &LateContext,
expr: &'e Expr)
-> Option<&'e Expr> {
match expr.node {
ExprMethodCall(_, _, ref args) => find_initial_receiver(cx, &args[0]),
ExprCall(..) if is_rocket_start_call(cx, expr) => Some(expr),
ExprCall(ref call, _) => find_initial_receiver(cx, call),
ExprPath(_) => Some(expr),
_ => None,
}
}
pub fn rocket_method_call<'e>(method: &str,
cx: &LateContext,
expr: &'e Expr)
-> (Option<(Option<&'e Expr>, &'e [Expr])>) {
if let ExprMethodCall(ref seg, _, ref exprs) = expr.node {
if seg.name.as_str() == method && is_impl_method(cx, expr, ROCKET_TYPE) {
let receiver = find_initial_receiver(cx, &exprs[0]);
return Some((receiver, &exprs[1..]));
}
}
None
}
pub fn is_rocket_start_call(cx: &LateContext, expr: &Expr) -> bool {
if let ExprCall(ref expr, ..) = expr.node {
if let ExprPath(ref qpath) = expr.node {
let def_id = cx.tables.qpath_def(qpath, expr.id).def_id();
if match_def_path(cx.tcx, def_id, ROCKET_IGNITE_FN) {
return true;
} else if match_def_path(cx.tcx, def_id, ROCKET_IGNITE_STATIC) {
return true;
} else if match_def_path(cx.tcx, def_id, ROCKET_CUSTOM_FN) {
return true;
} else if is_impl_method(cx, expr, ROCKET_CUSTOM_STATIC) {
return true;
}
}
}
false
}
pub fn extract_mount_fn_def_ids(cx: &LateContext, expr: &Expr) -> Vec<DefId> {
let mut output = Vec::new();
// Call to into_vec
if let ExprCall(_, ref args) = expr.node {
if let Some(&ExprBox(ref expr)) = args.iter().next().map(|e| &e.node) {
// Array of routes.
if let ExprArray(ref members) = expr.node {
for expr in members.iter() {
// Route::From call
if let ExprCall(_, ref args) = expr.node {
if args.len() < 1 {
continue;
}
// address of info struct
if let ExprAddrOf(_, ref expr) = args[0].node {
// path to info_struct
if let ExprPath(ref qpath) = expr.node {
let def = cx.tables.qpath_def(qpath, expr.id);
output.push(def.def_id());
}
}
}
}
}
}
}
output
}
pub fn returns_rocket_instance(cx: &LateContext, expr: &Expr) -> bool {
if let Some(ref ty) = cx.tables.expr_ty_opt(expr) {
if let Some(def_id) = ty.ty_to_def_id() {
if match_def_path(cx.tcx, def_id, ROCKET_TYPE) {
return true;
}
}
}
false
}
pub trait DefExt {
fn def_id_opt(&self) -> Option<DefId>;
}
impl DefExt for Def {
fn def_id_opt(&self) -> Option<DefId> {
match *self {
Def::Fn(id) | Def::Mod(id) | Def::Static(id, _) | Def::Variant(id)
| Def::VariantCtor(id, ..) | Def::Enum(id) | Def::TyAlias(id)
| Def::AssociatedTy(id) | Def::TyParam(id) | Def::Struct(id)
| Def::StructCtor(id, ..) | Def::Union(id) | Def::Trait(id)
| Def::Method(id) | Def::Const(id) | Def::AssociatedConst(id)
| Def::Local(id) | Def::Upvar(id, ..) | Def::Macro(id, ..)
| Def::GlobalAsm(id) => Some(id),
Def::Label(..) | Def::PrimTy(..) | Def::SelfTy(..) | Def::Err => None,
}
}
}
pub fn msg_and_help<'a, T: LintContext<'a>>(cx: &T,
lint: &'static Lint,
msg_sp: Span,
msg: &str,
note: &str,
help_sp: Option<Span>,
help: &str) {
// Be conservative. If we don't know the receiver, don't emit the warning.
if let Some(span) = help_sp {
cx.struct_span_lint(lint, msg_sp, msg)
.note(note)
.span_help(span, help)
.emit()
}
}

View File

@ -1,85 +0,0 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
#![allow(dead_code)]
#![deny(unmanaged_state)]
extern crate rocket;
use rocket::State;
struct MyType;
struct MySecondType;
mod external {
#[get("/state/extern")]
fn unmanaged(_c: ::State<i32>) { }
//~^ ERROR not currently being managed
#[get("/state/extern")]
fn managed(_c: ::State<u32>) { }
//~^ WARN is not mounted
#[get("/state/extern")]
fn unmanaged_unmounted(_c: ::State<u8>) { }
//~^ WARN is not mounted
//~^^ WARN is not mounted
}
#[get("/state/bad")]
fn unmanaged(_b: State<MySecondType>) { }
//~^ ERROR not currently being managed
//~^^ ERROR not currently being managed
#[get("/state/ok")]
fn managed(_a: State<u32>) { }
#[get("/state/bad")]
fn managed_two(_b: State<MyType>) { }
#[get("/state/ok")]
fn unmounted_doesnt_error(_a: State<i8>) { }
//~^ WARN is not mounted
//~^^ WARN is not mounted
#[get("/ignored")]
#[allow(unmanaged_state)]
fn ignored(_b: State<u16>) { }
//~^ WARN is not mounted
#[get("/unmounted/ignored")]
#[allow(unmounted_route)]
fn unmounted_ignored() { }
#[get("/mounted/nonce")]
fn mounted_only_once() { }
//~^ WARN is not mounted
fn main() {
rocket::ignite()
.mount("/", routes![managed, unmanaged, external::unmanaged])
.mount("/", routes![managed_two, ignored, mounted_only_once])
.manage(MyType)
.manage(100u32);
rocket::ignite()
.mount("/", routes![managed, unmanaged, external::unmanaged])
.mount("/", routes![external::managed, managed_two])
.manage(MyType)
.manage(100i32)
.manage(100u32);
let config = rocket::Config::development().unwrap();
// Ensure unresolved paths are handled properly by lint.
rocket::Rocket::custom(config, false)
.mount("/", routes![managed, unmanaged, external::unmanaged])
.mount("/", routes![managed_two, ignored, mounted_only_once])
.mount("/", routes![external::managed, external::unmanaged_unmounted])
.mount("/", routes![unmounted_doesnt_error, managed_two])
.manage(MyType)
.manage(MySecondType)
.manage(100i32)
.manage(100u32)
.manage(1u8)
.manage(1i8);
}

View File

@ -1,14 +0,0 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
#![allow(dead_code)]
#![deny(unmounted_route)]
extern crate rocket;
#[get("/")]
fn index() { }
//~^ ERROR is not mounted
fn main() {
rocket::ignite().launch();
}

View File

@ -1,7 +1,6 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
#![allow(dead_code)]
#![deny(unmounted_route)]
extern crate rocket;

View File

@ -1,7 +1,6 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
#![allow(dead_code, unused_variables)]
#![deny(unmounted_routes, unmanaged_state)]
extern crate rocket;

View File

@ -1,7 +1,6 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
#![allow(dead_code, unused_variables)]
#![deny(unmounted_route, unmanaged_state)]
extern crate rocket;

View File

@ -1,5 +1,3 @@
#![allow(unmanaged_state)]
use rocket::{self, State};
use rocket::fairing::AdHoc;
use rocket::config::{self, Config, Environment, LoggingLevel};

View File

@ -1,6 +1,5 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
#![allow(unmanaged_state)]
extern crate rocket;