diff --git a/maud/Cargo.toml b/maud/Cargo.toml
index cd4aec3..4d60103 100644
--- a/maud/Cargo.toml
+++ b/maud/Cargo.toml
@@ -1,16 +1,15 @@
 [package]
-
 name = "maud"
 # When releasing a new version, please update html_root_url in src/lib.rs
 version = "0.20.0"
 authors = ["Chris Wong <lambda.fairy@gmail.com>"]
-
 license = "MIT/Apache-2.0"
 documentation = "https://docs.rs/maud/"
 homepage = "https://maud.lambda.xyz/"
 repository = "https://github.com/lfairy/maud"
 description = "Compile-time HTML templates."
 categories = ["template-engine"]
+edition = "2018"
 
 [dependencies]
 maud_htmlescape = { version = "0.17.0", path = "../maud_htmlescape" }
diff --git a/maud/src/lib.rs b/maud/src/lib.rs
index fbe4118..e7b2cc8 100644
--- a/maud/src/lib.rs
+++ b/maud/src/lib.rs
@@ -13,9 +13,6 @@
 #[cfg(feature = "iron")] extern crate iron;
 #[cfg(feature = "rocket")] extern crate rocket;
 
-extern crate maud_htmlescape;
-extern crate maud_macros;
-
 use std::fmt::{self, Write};
 
 pub use maud_macros::{html, html_debug};
@@ -159,7 +156,7 @@ mod iron_support {
     use iron::modifier::{Modifier, Set};
     use iron::modifiers::Header;
     use iron::response::{Response, WriteBody};
-    use PreEscaped;
+    use crate::PreEscaped;
 
     impl Modifier<Response> for PreEscaped<String> {
         fn modify(self, response: &mut Response) {
@@ -182,7 +179,7 @@ mod rocket_support {
     use rocket::request::Request;
     use rocket::response::{Responder, Response};
     use std::io::Cursor;
-    use PreEscaped;
+    use crate::PreEscaped;
 
     impl Responder<'static> for PreEscaped<String> {
         fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
@@ -196,7 +193,7 @@ mod rocket_support {
 
 #[cfg(feature = "actix-web")]
 mod actix_support {
-    use PreEscaped;
+    use crate::PreEscaped;
     use actix_web::{Responder, HttpResponse, HttpRequest, Error};
 
     impl Responder for PreEscaped<String> {
diff --git a/maud/tests/basic_syntax.rs b/maud/tests/basic_syntax.rs
index cfce50c..e731202 100644
--- a/maud/tests/basic_syntax.rs
+++ b/maud/tests/basic_syntax.rs
@@ -1,7 +1,5 @@
 #![feature(proc_macro_hygiene)]
 
-extern crate maud;
-
 use maud::{Markup, html};
 
 #[test]
diff --git a/maud/tests/control_structures.rs b/maud/tests/control_structures.rs
index 83f61bf..b4b07ba 100644
--- a/maud/tests/control_structures.rs
+++ b/maud/tests/control_structures.rs
@@ -1,7 +1,5 @@
 #![feature(proc_macro_hygiene)]
 
-extern crate maud;
-
 use maud::html;
 
 #[test]
@@ -194,7 +192,7 @@ fn let_lexical_scope() {
 #[test]
 fn let_type_ascription() {
     let s = html! {
-        @let mut x: Box<Iterator<Item=u32>> = Box::new(vec![42].into_iter());
+        @let mut x: Box<dyn Iterator<Item=u32>> = Box::new(vec![42].into_iter());
         "I have " (x.next().unwrap()) " cupcakes!"
     }.into_string();
     assert_eq!(s, "I have 42 cupcakes!");
diff --git a/maud/tests/misc.rs b/maud/tests/misc.rs
index bfe307e..94c9084 100644
--- a/maud/tests/misc.rs
+++ b/maud/tests/misc.rs
@@ -1,8 +1,6 @@
 #![feature(proc_macro_hygiene)]
 
-extern crate maud;
-
-use maud::html;
+use maud::{self, html};
 
 #[test]
 fn issue_13() {
diff --git a/maud/tests/splices.rs b/maud/tests/splices.rs
index ad88d15..2b59334 100644
--- a/maud/tests/splices.rs
+++ b/maud/tests/splices.rs
@@ -1,7 +1,5 @@
 #![feature(proc_macro_hygiene)]
 
-extern crate maud;
-
 use maud::html;
 
 #[test]
diff --git a/maud/tests/warnings.rs b/maud/tests/warnings.rs
index 5e635f9..d836a29 100644
--- a/maud/tests/warnings.rs
+++ b/maud/tests/warnings.rs
@@ -1,4 +1,4 @@
-extern crate compiletest_rs;
+use compiletest_rs;
 
 use std::path::PathBuf;
 
diff --git a/maud_htmlescape/Cargo.toml b/maud_htmlescape/Cargo.toml
index a5caae0..36e3437 100644
--- a/maud_htmlescape/Cargo.toml
+++ b/maud_htmlescape/Cargo.toml
@@ -1,15 +1,14 @@
 [package]
-
 name = "maud_htmlescape"
 # When releasing a new version, please update html_root_url in lib.rs
 version = "0.17.0"
 authors = ["Chris Wong <lambda.fairy@gmail.com>"]
-
 license = "MIT/Apache-2.0"
 documentation = "https://docs.rs/maud_htmlescape/"
 homepage = "https://maud.lambda.xyz/"
 repository = "https://github.com/lfairy/maud"
 description = "Internal support code used by Maud."
+edition = "2018"
 
 [lib]
 path = "lib.rs"
diff --git a/maud_htmlescape/lib.rs b/maud_htmlescape/lib.rs
index 873df13..5ab8535 100644
--- a/maud_htmlescape/lib.rs
+++ b/maud_htmlescape/lib.rs
@@ -57,7 +57,7 @@ impl<'a> fmt::Write for Escaper<'a> {
 #[cfg(test)]
 mod test {
     use std::fmt::Write;
-    use Escaper;
+    use crate::Escaper;
 
     #[test]
     fn it_works() {
diff --git a/maud_macros/Cargo.toml b/maud_macros/Cargo.toml
index 154f908..9ecaeb4 100644
--- a/maud_macros/Cargo.toml
+++ b/maud_macros/Cargo.toml
@@ -1,15 +1,14 @@
 [package]
-
 name = "maud_macros"
 # When releasing a new version, please update html_root_url in src/lib.rs
 version = "0.20.0"
 authors = ["Chris Wong <lambda.fairy@gmail.com>"]
-
 license = "MIT/Apache-2.0"
 documentation = "https://docs.rs/maud_macros/"
 homepage = "https://maud.lambda.xyz/"
 repository = "https://github.com/lfairy/maud"
 description = "Compile-time HTML templates."
+edition = "2018"
 
 [dependencies]
 literalext = { version = "0.1", default-features = false, features = ["proc-macro"] }
diff --git a/maud_macros/src/generate.rs b/maud_macros/src/generate.rs
index 8a066b4..d48a860 100644
--- a/maud_macros/src/generate.rs
+++ b/maud_macros/src/generate.rs
@@ -1,3 +1,4 @@
+use matches::matches;
 use maud_htmlescape::Escaper;
 use proc_macro::{
     Delimiter,
@@ -10,7 +11,7 @@ use proc_macro::{
     TokenTree,
 };
 
-use ast::*;
+use crate::ast::*;
 
 pub fn generate(markups: Vec<Markup>, output_ident: TokenTree) -> TokenStream {
     let mut build = Builder::new(output_ident.clone());
diff --git a/maud_macros/src/lib.rs b/maud_macros/src/lib.rs
index 0423aaf..010e3cd 100644
--- a/maud_macros/src/lib.rs
+++ b/maud_macros/src/lib.rs
@@ -10,9 +10,6 @@
 // lifetimes outweighs the marginal gains from explicit borrowing
 #![allow(clippy::needless_pass_by_value)]
 
-extern crate literalext;
-#[macro_use] extern crate matches;
-extern crate maud_htmlescape;
 extern crate proc_macro;
 
 mod ast;
diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs
index 342cf84..15a96de 100644
--- a/maud_macros/src/parse.rs
+++ b/maud_macros/src/parse.rs
@@ -11,8 +11,8 @@ use std::mem;
 
 use literalext::LiteralExt;
 
-use ast;
-use ParseResult;
+use crate::ast;
+use crate::ParseResult;
 
 pub fn parse(input: TokenStream) -> ParseResult<Vec<ast::Markup>> {
     Parser::new(input).markups()