about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcore/cell.rs2
-rw-r--r--src/libcore/flate.rs2
-rw-r--r--src/libcore/path.rs2
-rw-r--r--src/libcore/rand.rs2
-rw-r--r--src/libcore/repr.rs4
-rw-r--r--src/libcore/rt/uv/mod.rs2
-rw-r--r--src/libcore/rt/uv/net.rs2
-rw-r--r--src/libcore/rt/uvio.rs2
-rw-r--r--src/libcore/run.rs2
-rw-r--r--src/libcore/str.rs6
-rw-r--r--src/libcore/task/spawn.rs2
-rw-r--r--src/libcore/unstable/extfmt.rs4
-rw-r--r--src/libcore/vec.rs4
-rw-r--r--src/librustc/back/link.rs6
-rw-r--r--src/librustc/driver/driver.rs2
-rw-r--r--src/librustc/metadata/encoder.rs2
-rw-r--r--src/librustc/middle/borrowck/check_loans.rs13
-rw-r--r--src/librustc/middle/borrowck/gather_loans.rs2
-rw-r--r--src/librustc/middle/borrowck/loan.rs12
-rw-r--r--src/librustc/middle/check_match.rs2
-rw-r--r--src/librustc/middle/lint.rs57
-rw-r--r--src/librustc/middle/liveness.rs43
-rw-r--r--src/librustc/middle/mem_categorization.rs10
-rw-r--r--src/librustc/middle/resolve.rs18
-rw-r--r--src/librustc/middle/trans/_match.rs2
-rw-r--r--src/librustc/middle/trans/callee.rs6
-rw-r--r--src/librustc/middle/trans/closure.rs2
-rw-r--r--src/librustc/middle/trans/expr.rs6
-rw-r--r--src/librustc/middle/trans/foreign.rs3
-rw-r--r--src/librustc/middle/trans/monomorphize.rs2
-rw-r--r--src/librustc/middle/ty.rs6
-rw-r--r--src/librustc/middle/typeck/check/method.rs2
-rw-r--r--src/librustc/middle/typeck/check/mod.rs9
-rw-r--r--src/librustc/middle/typeck/check/vtable.rs2
-rw-r--r--src/librustc/middle/typeck/rscope.rs2
-rw-r--r--src/libstd/bitv.rs2
-rw-r--r--src/libstd/deque.rs8
-rw-r--r--src/libstd/dlist.rs8
-rw-r--r--src/libstd/num/rational.rs4
-rw-r--r--src/libstd/rope.rs5
-rw-r--r--src/libstd/sha1.rs4
-rw-r--r--src/libstd/sort.rs4
-rw-r--r--src/libstd/test.rs3
-rw-r--r--src/libstd/time.rs4
-rw-r--r--src/libsyntax/diagnostic.rs2
-rw-r--r--src/libsyntax/ext/fmt.rs8
-rw-r--r--src/libsyntax/parse/attr.rs8
-rw-r--r--src/libsyntax/parse/comments.rs2
-rw-r--r--src/libsyntax/parse/parser.rs28
-rw-r--r--src/libsyntax/print/pp.rs6
-rw-r--r--src/libsyntax/print/pprust.rs2
-rw-r--r--src/test/compile-fail/unused-mut-variables.rs42
52 files changed, 265 insertions, 120 deletions
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs
index 8c2175eeaca..27e03d2bf31 100644
--- a/src/libcore/cell.rs
+++ b/src/libcore/cell.rs
@@ -121,7 +121,7 @@ fn test_with_ref() {
 #[test]
 fn test_with_mut_ref() {
     let good = ~[1, 2, 3];
-    let mut v = ~[1, 2];
+    let v = ~[1, 2];
     let c = Cell(v);
     do c.with_mut_ref() |v| { v.push(3); }
     let v = c.take();
diff --git a/src/libcore/flate.rs b/src/libcore/flate.rs
index a4f12f7da7e..70c96c9c806 100644
--- a/src/libcore/flate.rs
+++ b/src/libcore/flate.rs
@@ -67,7 +67,7 @@ pub fn deflate_bytes(bytes: &const [u8]) -> ~[u8] {
 pub fn inflate_bytes(bytes: &const [u8]) -> ~[u8] {
     do vec::as_const_buf(bytes) |b, len| {
         unsafe {
-            let mut outsz : size_t = 0;
+            let outsz : size_t = 0;
             let res =
                 rustrt::tinfl_decompress_mem_to_heap(b as *c_void,
                                                      len as size_t,
diff --git a/src/libcore/path.rs b/src/libcore/path.rs
index 23f7e912f96..8328d42c35e 100644
--- a/src/libcore/path.rs
+++ b/src/libcore/path.rs
@@ -854,7 +854,7 @@ pub mod windows {
             while i < s.len() {
                 if is_sep(s[i]) {
                     let pre = s.slice(2, i).to_owned();
-                    let mut rest = s.slice(i, s.len()).to_owned();
+                    let rest = s.slice(i, s.len()).to_owned();
                     return Some((pre, rest));
                 }
                 i += 1;
diff --git a/src/libcore/rand.rs b/src/libcore/rand.rs
index 1e33f382df5..919c7bbb036 100644
--- a/src/libcore/rand.rs
+++ b/src/libcore/rand.rs
@@ -742,7 +742,7 @@ struct XorShiftState {
 impl Rng for XorShiftState {
     fn next(&self) -> u32 {
         let x = self.x;
-        let mut t = x ^ (x << 11);
+        let t = x ^ (x << 11);
         self.x = self.y;
         self.y = self.z;
         self.z = self.w;
diff --git a/src/libcore/repr.rs b/src/libcore/repr.rs
index ca67e6366b5..03e44e00d88 100644
--- a/src/libcore/repr.rs
+++ b/src/libcore/repr.rs
@@ -210,7 +210,7 @@ pub impl ReprVisitor {
     #[inline(always)]
     fn visit_ptr_inner(&self, ptr: *c_void, inner: *TyDesc) -> bool {
         unsafe {
-            let mut u = ReprVisitor(ptr, self.writer);
+            let u = ReprVisitor(ptr, self.writer);
             let v = reflect::MovePtrAdaptor(u);
             visit_tydesc(inner, @v as @TyVisitor);
             true
@@ -667,7 +667,7 @@ pub fn write_repr<T>(writer: @Writer, object: &T) {
     unsafe {
         let ptr = ptr::to_unsafe_ptr(object) as *c_void;
         let tydesc = intrinsic::get_tydesc::<T>();
-        let mut u = ReprVisitor(ptr, writer);
+        let u = ReprVisitor(ptr, writer);
         let v = reflect::MovePtrAdaptor(u);
         visit_tydesc(tydesc, @v as @TyVisitor)
     }
diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs
index 32757d6376e..4cbc8d70569 100644
--- a/src/libcore/rt/uv/mod.rs
+++ b/src/libcore/rt/uv/mod.rs
@@ -402,7 +402,7 @@ fn loop_smoke_test() {
 fn idle_new_then_close() {
     do run_in_bare_thread {
         let mut loop_ = Loop::new();
-        let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
+        let idle_watcher = { IdleWatcher::new(&mut loop_) };
         idle_watcher.close();
     }
 }
diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs
index b4a08c14928..bcfe8b2cfdf 100644
--- a/src/libcore/rt/uv/net.rs
+++ b/src/libcore/rt/uv/net.rs
@@ -393,7 +393,7 @@ fn connect_read() {
                 let buf = vec_from_uv_buf(buf);
                 rtdebug!("read cb!");
                 if status.is_none() {
-                    let bytes = buf.unwrap();
+                    let _bytes = buf.unwrap();
                     rtdebug!("%s", bytes.slice(0, nread as uint).to_str());
                 } else {
                     rtdebug!("status after read: %s", status.get().to_str());
diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index ff539739835..d4e547de383 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -206,7 +206,7 @@ impl TcpListener for UvTcpListener {
                     let mut server_stream_watcher = server_stream_watcher;
                     let mut loop_ = loop_from_watcher(&server_stream_watcher);
                     let mut client_tcp_watcher = TcpWatcher::new(&mut loop_);
-                    let mut client_tcp_watcher = client_tcp_watcher.as_stream();
+                    let client_tcp_watcher = client_tcp_watcher.as_stream();
                     // XXX: Need's to be surfaced in interface
                     server_stream_watcher.accept(client_tcp_watcher);
                     Some(~UvStream::new(client_tcp_watcher))
diff --git a/src/libcore/run.rs b/src/libcore/run.rs
index 087ee6cdf85..f9d7f4a229c 100644
--- a/src/libcore/run.rs
+++ b/src/libcore/run.rs
@@ -343,7 +343,7 @@ pub fn start_program(prog: &str, args: &[~str]) -> @Program {
         fn force_destroy(&mut self) { destroy_repr(&mut self.r, true); }
     }
 
-    let mut repr = ProgRepr {
+    let repr = ProgRepr {
         pid: pid,
         in_fd: pipe_input.out,
         out_file: os::fdopen(pipe_output.in),
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index f1cacc6a348..38d68175679 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -673,7 +673,7 @@ pub fn levdistance(s: &str, t: &str) -> uint {
 
         for t.each_chari |j, tc| {
 
-            let mut next = dcol[j + 1];
+            let next = dcol[j + 1];
 
             if sc == tc {
                 dcol[j + 1] = current;
@@ -909,7 +909,7 @@ impl TotalOrd for @str {
 /// Bytewise slice less than
 fn lt(a: &str, b: &str) -> bool {
     let (a_len, b_len) = (a.len(), b.len());
-    let mut end = uint::min(a_len, b_len);
+    let end = uint::min(a_len, b_len);
 
     let mut i = 0;
     while i < end {
@@ -1715,7 +1715,7 @@ pub fn utf16_chars(v: &[u16], f: &fn(char)) {
     let len = vec::len(v);
     let mut i = 0u;
     while (i < len && v[i] != 0u16) {
-        let mut u = v[i];
+        let u = v[i];
 
         if  u <= 0xD7FF_u16 || u >= 0xE000_u16 {
             f(u as char);
diff --git a/src/libcore/task/spawn.rs b/src/libcore/task/spawn.rs
index 118c4cc2312..d872d38a278 100644
--- a/src/libcore/task/spawn.rs
+++ b/src/libcore/task/spawn.rs
@@ -575,7 +575,7 @@ fn spawn_raw_oldsched(opts: TaskOpts, f: ~fn()) {
             };
             assert!(!new_task.is_null());
             // Getting killed after here would leak the task.
-            let mut notify_chan = if opts.notify_chan.is_none() {
+            let notify_chan = if opts.notify_chan.is_none() {
                 None
             } else {
                 Some(opts.notify_chan.swap_unwrap())
diff --git a/src/libcore/unstable/extfmt.rs b/src/libcore/unstable/extfmt.rs
index ad3dce0a749..ee33e2ed20b 100644
--- a/src/libcore/unstable/extfmt.rs
+++ b/src/libcore/unstable/extfmt.rs
@@ -538,7 +538,7 @@ pub mod rt {
     pub fn conv_str(cv: Conv, s: &str, buf: &mut ~str) {
         // For strings, precision is the maximum characters
         // displayed
-        let mut unpadded = match cv.precision {
+        let unpadded = match cv.precision {
           CountImplied => s,
           CountIs(max) => if (max as uint) < str::char_len(s) {
             str::slice(s, 0, max as uint)
@@ -596,7 +596,7 @@ pub mod rt {
     #[deriving(Eq)]
     pub enum PadMode { PadSigned, PadUnsigned, PadNozero, PadFloat }
 
-    pub fn pad(cv: Conv, mut s: &str, head: Option<char>, mode: PadMode,
+    pub fn pad(cv: Conv, s: &str, head: Option<char>, mode: PadMode,
                buf: &mut ~str) {
         let headsize = match head { Some(_) => 1, _ => 0 };
         let uwidth : uint = match cv.width {
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 1ef567e9cef..e478936ff65 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -1755,7 +1755,7 @@ impl<T: TotalOrd> TotalOrd for @[T] {
 
 fn lt<T:Ord>(a: &[T], b: &[T]) -> bool {
     let (a_len, b_len) = (a.len(), b.len());
-    let mut end = uint::min(a_len, b_len);
+    let end = uint::min(a_len, b_len);
 
     let mut i = 0;
     while i < end {
@@ -3897,7 +3897,7 @@ mod tests {
 
     #[test]
     fn reversed_mut() {
-        let mut v2 = reversed::<int>(~[10, 20]);
+        let v2 = reversed::<int>(~[10, 20]);
         assert!(v2[0] == 20);
         assert!(v2[1] == 10);
     }
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index 3dfa318826b..a176b0163a4 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -273,7 +273,7 @@ pub mod write {
                 let LLVMOptDefault    = 2 as c_int; // -O2, -Os
                 let LLVMOptAggressive = 3 as c_int; // -O3
 
-                let mut CodeGenOptLevel = match opts.optimize {
+                let CodeGenOptLevel = match opts.optimize {
                   session::No => LLVMOptNone,
                   session::Less => LLVMOptLess,
                   session::Default => LLVMOptDefault,
@@ -294,7 +294,7 @@ pub mod write {
                     return;
                 }
 
-                let mut FileType;
+                let FileType;
                 if output_type == output_type_object ||
                        output_type == output_type_exe {
                    FileType = lib::llvm::ObjectFile;
@@ -820,7 +820,7 @@ pub fn link_binary(sess: Session,
     cc_args.push(output.to_str());
     cc_args.push(obj_filename.to_str());
 
-    let mut lib_cmd;
+    let lib_cmd;
     let os = sess.targ_cfg.os;
     if os == session::os_macos {
         lib_cmd = ~"-dynamiclib";
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index fae73d7faf2..c4f37c2170d 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -349,7 +349,7 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg,
                 outputs: Option<@OutputFilenames>)
     -> (@ast::crate, Option<ty::ctxt>) {
     let time_passes = sess.time_passes();
-    let mut crate = time(time_passes, ~"parsing",
+    let crate = time(time_passes, ~"parsing",
                          || parse_input(sess, copy cfg, input) );
     if upto == cu_parse { return (crate, None); }
 
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index aaafc7c18d6..8515e0c6e9b 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -1341,7 +1341,7 @@ pub static metadata_encoding_version : &'static [u8] =
 
 pub fn encode_metadata(parms: EncodeParams, crate: &crate) -> ~[u8] {
     let wr = @io::BytesWriter();
-    let mut stats = Stats {
+    let stats = Stats {
         inline_bytes: 0,
         attr_bytes: 0,
         dep_bytes: 0,
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
index 4766fe1fb94..6f3075717ed 100644
--- a/src/librustc/middle/borrowck/check_loans.rs
+++ b/src/librustc/middle/borrowck/check_loans.rs
@@ -367,7 +367,18 @@ pub impl CheckLoanCtxt {
             // are only assigned once
         } else {
             match cmt.mutbl {
-                McDeclared | McInherited => { /*ok*/ }
+                McDeclared | McInherited => {
+                    // Ok, but if this loan is a mutable loan, then mark the
+                    // loan path (if it exists) as being used. This is similar
+                    // to the check performed in loan.rs in issue_loan(). This
+                    // type of use of mutable is different from issuing a loan,
+                    // however.
+                    for cmt.lp.each |lp| {
+                        for lp.node_id().each |&id| {
+                            self.tcx().used_mut_nodes.insert(id);
+                        }
+                    }
+                }
                 McReadOnly | McImmutable => {
                     self.bccx.span_err(
                         ex.span,
diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs
index 4f2e41dca5c..b8e0bba6b23 100644
--- a/src/librustc/middle/borrowck/gather_loans.rs
+++ b/src/librustc/middle/borrowck/gather_loans.rs
@@ -305,7 +305,7 @@ pub impl GatherLoanCtxt {
                 let mcx = &mem_categorization_ctxt {
                     tcx: self.tcx(),
                     method_map: self.bccx.method_map};
-                let mut cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
+                let cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
                 debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt));
 
                 match autoref.kind {
diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs
index 15189a552fb..aedd6bb5467 100644
--- a/src/librustc/middle/borrowck/loan.rs
+++ b/src/librustc/middle/borrowck/loan.rs
@@ -274,7 +274,17 @@ pub impl LoanContext {
         if !owns_lent_data ||
             self.bccx.is_subregion_of(self.scope_region, scope_ub)
         {
-            if loan_kind.is_take() && !cmt.mutbl.is_mutable() {
+            if cmt.mutbl.is_mutable() {
+                // If this loan is a mutable loan, then mark the loan path (if
+                // it exists) as being used. This is similar to the check
+                // performed in check_loans.rs in check_assignment(), but this
+                // is for a different purpose of having the 'mut' qualifier.
+                for cmt.lp.each |lp| {
+                    for lp.node_id().each |&id| {
+                        self.tcx().used_mut_nodes.insert(id);
+                    }
+                }
+            } else if loan_kind.is_take() {
                 // We do not allow non-mutable data to be "taken"
                 // under any circumstances.
                 return Err(bckerr {
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 1db8f8cc8de..8fc94cf51e2 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -481,7 +481,7 @@ pub fn specialize(cx: @MatchCheckCtxt,
                   left_ty: ty::t)
                -> Option<~[@pat]> {
     // Sad, but I can't get rid of this easily
-    let mut r0 = copy *raw_pat(r[0]);
+    let r0 = copy *raw_pat(r[0]);
     match r0 {
         pat{id: pat_id, node: n, span: pat_span} =>
             match n {
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 9087afe5969..bda97f4f530 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -13,6 +13,7 @@ use core::prelude::*;
 use driver::session::Session;
 use driver::session;
 use middle::ty;
+use middle::pat_util;
 use util::ppaux::{ty_to_str};
 
 use core::hashmap::HashMap;
@@ -86,6 +87,7 @@ pub enum lint {
 
     unused_variable,
     dead_assignment,
+    unused_mut,
 }
 
 pub fn level_to_str(lv: level) -> &'static str {
@@ -277,6 +279,13 @@ pub fn get_lint_dict() -> LintDict {
             desc: "detect assignments that will never be read",
             default: warn
         }),
+
+        (~"unused_mut",
+         LintSpec {
+            lint: unused_mut,
+            desc: "detect mut variables which don't need to be mutable",
+            default: warn
+        }),
     ];
     let mut map = HashMap::new();
     do vec::consume(v) |_, (k, v)| {
@@ -499,6 +508,7 @@ fn check_item(i: @ast::item, cx: ty::ctxt) {
     check_item_deprecated_mutable_fields(cx, i);
     check_item_deprecated_drop(cx, i);
     check_item_unused_unsafe(cx, i);
+    check_item_unused_mut(cx, i);
 }
 
 // Take a visitor, and modify it so that it will not proceed past subitems.
@@ -954,6 +964,53 @@ fn check_item_unused_unsafe(cx: ty::ctxt, it: @ast::item) {
     visit::visit_item(it, (), visit);
 }
 
+fn check_item_unused_mut(tcx: ty::ctxt, it: @ast::item) {
+    let check_pat: @fn(@ast::pat) = |p| {
+        let mut used = false;
+        let mut bindings = 0;
+        do pat_util::pat_bindings(tcx.def_map, p) |_, id, _, _| {
+            used = used || tcx.used_mut_nodes.contains(&id);
+            bindings += 1;
+        }
+        if !used {
+            let msg = if bindings == 1 {
+                ~"variable does not need to be mutable"
+            } else {
+                ~"variables do not need to be mutable"
+            };
+            tcx.sess.span_lint(unused_mut, p.id, it.id, p.span, msg);
+        }
+    };
+
+    let visit_fn_decl: @fn(&ast::fn_decl) = |fd| {
+        for fd.inputs.each |arg| {
+            if arg.is_mutbl {
+                check_pat(arg.pat);
+            }
+        }
+    };
+
+    let visit = item_stopping_visitor(
+        visit::mk_simple_visitor(@visit::SimpleVisitor {
+            visit_local: |l| {
+                if l.node.is_mutbl {
+                    check_pat(l.node.pat);
+                }
+            },
+            visit_fn: |_, fd, _, _, _| visit_fn_decl(fd),
+            visit_ty_method: |tm| visit_fn_decl(&tm.decl),
+            visit_struct_method: |sm| visit_fn_decl(&sm.decl),
+            visit_trait_method: |tm| {
+                match *tm {
+                    ast::required(ref tm) => visit_fn_decl(&tm.decl),
+                    ast::provided(m) => visit_fn_decl(&m.decl),
+                }
+            },
+            .. *visit::default_simple_visitor()
+        }));
+    visit::visit_item(it, (), visit);
+}
+
 fn check_fn(tcx: ty::ctxt, fk: &visit::fn_kind, decl: &ast::fn_decl,
             _body: &ast::blk, span: span, id: ast::node_id) {
     debug!("lint check_fn fk=%? id=%?", fk, id);
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 3136d7bf4e4..2b36ce4ce03 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -1516,9 +1516,8 @@ fn check_local(local: @local, self: @Liveness, vt: vt<@Liveness>) {
 
         // Initializer:
         self.warn_about_unused_or_dead_vars_in_pat(local.node.pat);
-        if !local.node.is_mutbl {
-            self.check_for_reassignments_in_pat(local.node.pat);
-        }
+        self.check_for_reassignments_in_pat(local.node.pat,
+                                            local.node.is_mutbl);
       }
       None => {
 
@@ -1702,12 +1701,15 @@ pub impl Liveness {
         match expr.node {
           expr_path(_) => {
             match *self.tcx.def_map.get(&expr.id) {
-              def_local(nid, false) => {
-                // Assignment to an immutable variable or argument:
-                // only legal if there is no later assignment.
+              def_local(nid, mutbl) => {
+                // Assignment to an immutable variable or argument: only legal
+                // if there is no later assignment. If this local is actually
+                // mutable, then check for a reassignment to flag the mutability
+                // as being used.
                 let ln = self.live_node(expr.id, expr.span);
                 let var = self.variable(nid, expr.span);
-                self.check_for_reassignment(ln, var, expr.span);
+                self.check_for_reassignment(ln, var, expr.span,
+                                            if mutbl {Some(nid)} else {None});
                 self.warn_about_dead_assign(expr.span, expr.id, ln, var);
               }
               def => {
@@ -1731,23 +1733,28 @@ pub impl Liveness {
        }
     }
 
-    fn check_for_reassignments_in_pat(@self, pat: @pat) {
-        do self.pat_bindings(pat) |ln, var, sp, _id| {
-            self.check_for_reassignment(ln, var, sp);
+    fn check_for_reassignments_in_pat(@self, pat: @pat, mutbl: bool) {
+        do self.pat_bindings(pat) |ln, var, sp, id| {
+            self.check_for_reassignment(ln, var, sp,
+                                        if mutbl {Some(id)} else {None});
         }
     }
 
     fn check_for_reassignment(@self, ln: LiveNode, var: Variable,
-                              orig_span: span) {
+                              orig_span: span, mutbl: Option<node_id>) {
         match self.assigned_on_exit(ln, var) {
           Some(ExprNode(span)) => {
-            self.tcx.sess.span_err(
-                span,
-                ~"re-assignment of immutable variable");
-
-            self.tcx.sess.span_note(
-                orig_span,
-                ~"prior assignment occurs here");
+            match mutbl {
+              Some(id) => { self.tcx.used_mut_nodes.insert(id); }
+              None => {
+                self.tcx.sess.span_err(
+                    span,
+                    ~"re-assignment of immutable variable");
+                self.tcx.sess.span_note(
+                    orig_span,
+                    ~"prior assignment occurs here");
+              }
+            }
           }
           Some(lnk) => {
             self.tcx.sess.span_bug(
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 6473cb8e8e0..51e6860432a 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -351,6 +351,16 @@ pub impl MutabilityCategory {
     }
 }
 
+pub impl loan_path {
+    fn node_id(&self) -> Option<ast::node_id> {
+        match *self {
+            lp_local(id) | lp_arg(id) => Some(id),
+            lp_deref(lp, _) | lp_comp(lp, _) => lp.node_id(),
+            lp_self => None
+        }
+    }
+}
+
 pub impl mem_categorization_ctxt {
     fn cat_expr(&self, expr: @ast::expr) -> cmt {
         match self.tcx.adjustments.find(&expr.id) {
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 41b372119cd..43c6a184d13 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -960,7 +960,7 @@ pub impl Resolver {
         // child name directly. Otherwise, we create or reuse an anonymous
         // module and add the child to that.
 
-        let mut module_;
+        let module_;
         match reduced_graph_parent {
             ModuleReducedGraphParent(parent_module) => {
                 module_ = parent_module;
@@ -1527,7 +1527,7 @@ pub impl Resolver {
                                      block: &blk,
                                      parent: ReducedGraphParent,
                                      visitor: vt<ReducedGraphParent>) {
-        let mut new_parent;
+        let new_parent;
         if self.block_needs_anonymous_module(block) {
             let block_id = block.node.id;
 
@@ -2427,7 +2427,7 @@ pub impl Resolver {
 
         let merge_import_resolution = |ident,
                                        name_bindings: @mut NameBindings| {
-            let mut dest_import_resolution;
+            let dest_import_resolution;
             match module_.import_resolutions.find(ident) {
                 None => {
                     // Create a new import resolution from this child.
@@ -2583,8 +2583,8 @@ pub impl Resolver {
         let module_prefix_result = self.resolve_module_prefix(module_,
                                                               module_path);
 
-        let mut search_module;
-        let mut start_index;
+        let search_module;
+        let start_index;
         match module_prefix_result {
             Failed => {
                 self.session.span_err(span, ~"unresolved name");
@@ -3221,7 +3221,7 @@ pub impl Resolver {
                 allow_capturing_self: AllowCapturingSelfFlag)
              -> Option<def_like> {
         let mut def;
-        let mut is_ty_param;
+        let is_ty_param;
 
         match def_like {
             dl_def(d @ def_local(*)) | dl_def(d @ def_upvar(*)) |
@@ -4530,7 +4530,7 @@ pub impl Resolver {
                                  -> Option<def> {
         let module_path_idents = self.intern_module_part_of_path(path);
 
-        let mut containing_module;
+        let containing_module;
         match self.resolve_module_path_for_import(self.current_module,
                                                   module_path_idents,
                                                   UseLexicalScope,
@@ -4578,7 +4578,7 @@ pub impl Resolver {
 
         let root_module = self.graph_root.get_module();
 
-        let mut containing_module;
+        let containing_module;
         match self.resolve_module_path_from_root(root_module,
                                                  module_path_idents,
                                                  0,
@@ -4622,7 +4622,7 @@ pub impl Resolver {
                                         span: span)
                                      -> Option<def> {
         // Check the local set of ribs.
-        let mut search_result;
+        let search_result;
         match namespace {
             ValueNS => {
                 search_result = self.search_ribs(&mut self.value_ribs, ident,
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index 7339003c614..dc59fcecb5a 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -248,7 +248,7 @@ pub enum opt_result {
 pub fn trans_opt(bcx: block, o: &Opt) -> opt_result {
     let _icx = bcx.insn_ctxt("match::trans_opt");
     let ccx = bcx.ccx();
-    let mut bcx = bcx;
+    let bcx = bcx;
     match *o {
         lit(ExprLit(lit_expr)) => {
             let datumblock = expr::trans_to_datum(bcx, lit_expr);
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index 3301ed62cbe..7a174be1e57 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -292,7 +292,7 @@ pub fn trans_fn_ref_with_vtables(
     }
 
     // Find the actual function pointer.
-    let mut val = {
+    let val = {
         if def_id.crate == ast::local_crate {
             // Internal reference.
             get_item_val(ccx, def_id.node)
@@ -415,7 +415,7 @@ pub fn trans_lang_call_with_type_params(bcx: block,
                                                     type_params,
                                                     None,
                                                     fty);
-                    let mut llfnty = type_of::type_of(callee.bcx.ccx(),
+                    let llfnty = type_of::type_of(callee.bcx.ccx(),
                                                       substituted);
                     new_llval = PointerCast(callee.bcx, fn_data.llfn, llfnty);
                 }
@@ -712,7 +712,7 @@ pub fn trans_arg_expr(bcx: block,
         }
     };
     let mut arg_datum = arg_datumblock.datum;
-    let mut bcx = arg_datumblock.bcx;
+    let bcx = arg_datumblock.bcx;
 
     debug!("   arg datum: %s", arg_datum.to_str(bcx.ccx()));
 
diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs
index cb815506c39..4fc4cae464d 100644
--- a/src/librustc/middle/trans/closure.rs
+++ b/src/librustc/middle/trans/closure.rs
@@ -261,7 +261,7 @@ pub fn build_closure(bcx0: block,
                      include_ret_handle: Option<ValueRef>) -> ClosureResult {
     let _icx = bcx0.insn_ctxt("closure::build_closure");
     // If we need to, package up the iterator body to call
-    let mut bcx = bcx0;;
+    let bcx = bcx0;;
     let ccx = bcx.ccx(), tcx = ccx.tcx;
 
     // Package up the captured upvars
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 4b4d0869a3e..8727db27fff 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -192,7 +192,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
         }
         Some(&@AutoAddEnv(*)) => {
             let mut bcx = bcx;
-            let mut datum = unpack_datum!(bcx, {
+            let datum = unpack_datum!(bcx, {
                 trans_to_datum_unadjusted(bcx, expr)
             });
             add_env(bcx, expr, datum)
@@ -1187,7 +1187,7 @@ fn trans_rec_or_struct(bcx: block,
                        dest: Dest) -> block
 {
     let _icx = bcx.insn_ctxt("trans_rec");
-    let mut bcx = bcx;
+    let bcx = bcx;
 
     let ty = node_id_type(bcx, id);
     let tcx = bcx.tcx();
@@ -1505,7 +1505,7 @@ fn trans_lazy_binop(bcx: block,
                     b: @ast::expr) -> DatumBlock {
     let _icx = bcx.insn_ctxt("trans_lazy_binop");
     let binop_ty = expr_ty(bcx, binop_expr);
-    let mut bcx = bcx;
+    let bcx = bcx;
 
     let Result {bcx: past_lhs, val: lhs} = {
         do base::with_scope_result(bcx, a.info(), ~"lhs") |bcx| {
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 956ee3bf144..cb15a2e8c64 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -567,7 +567,8 @@ pub fn trans_intrinsic(ccx: @CrateContext,
         set_fixed_stack_segment(fcx.llfn);
     }
 
-    let mut bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
+    let mut bcx = top_scope_block(fcx, None);
+    let lltop = bcx.llbb;
     match *ccx.sess.str_of(item.ident) {
         ~"atomic_cxchg" => {
             let old = AtomicCmpXchg(bcx,
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index a0fcdf7fde8..052bea1b022 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -102,7 +102,7 @@ pub fn monomorphic_fn(ccx: @CrateContext,
     }
 
     let tpt = ty::lookup_item_type(ccx.tcx, fn_id);
-    let mut llitem_ty = tpt.ty;
+    let llitem_ty = tpt.ty;
 
     let map_node = session::expect(ccx.sess, ccx.tcx.items.find(&fn_id.node),
      || fmt!("While monomorphizing %?, couldn't find it in the item map \
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 033fbecc08b..7cdd7c8a6f2 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -304,6 +304,11 @@ struct ctxt_ {
     // Set of used unsafe nodes (functions or blocks). Unsafe nodes not
     // present in this set can be warned about.
     used_unsafe: @mut HashSet<ast::node_id>,
+
+    // Set of nodes which mark locals as mutable which end up getting used at
+    // some point. Local variable definitions not in this set can be warned
+    // about.
+    used_mut_nodes: @mut HashSet<ast::node_id>,
 }
 
 pub enum tbox_flag {
@@ -933,6 +938,7 @@ pub fn mk_ctxt(s: session::Session,
         destructors: @mut HashSet::new(),
         trait_impls: @mut HashMap::new(),
         used_unsafe: @mut HashSet::new(),
+        used_mut_nodes: @mut HashSet::new(),
      }
 }
 
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index 9d8a1145f22..e778986b2d1 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -180,7 +180,7 @@ pub struct Candidate {
 
 pub impl<'self> LookupContext<'self> {
     fn do_lookup(&self, self_ty: ty::t) -> Option<method_map_entry> {
-        let mut self_ty = structurally_resolved_type(self.fcx,
+        let self_ty = structurally_resolved_type(self.fcx,
                                                      self.self_expr.span,
                                                      self_ty);
 
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index d6f892cac3c..ca9b3602d5d 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -1625,7 +1625,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         // block syntax lambdas; that is, lambdas without explicit
         // sigils.
         let expected_sty = unpack_expected(fcx, expected, |x| Some(copy *x));
-        let mut error_happened = false;
+        let error_happened = false;
         let (expected_sig,
              expected_purity,
              expected_sigil,
@@ -1706,7 +1706,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
                    field: ast::ident,
                    tys: &[@ast::Ty]) {
         let tcx = fcx.ccx.tcx;
-        let mut bot = check_expr(fcx, base);
+        let bot = check_expr(fcx, base);
         let expr_t = structurally_resolved_type(fcx, expr.span,
                                                 fcx.expr_ty(base));
         let (base_t, derefs) = do_autoderef(fcx, expr.span, expr_t);
@@ -2867,7 +2867,7 @@ pub fn check_decl_local(fcx: @mut FnCtxt, local: @ast::local)  {
 }
 
 pub fn check_stmt(fcx: @mut FnCtxt, stmt: @ast::stmt)  {
-    let mut node_id;
+    let node_id;
     let mut saw_bot = false;
     let mut saw_err = false;
     match stmt.node {
@@ -3124,7 +3124,8 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
     ccx.tcx.enum_var_cache.insert(local_def(id), @variants);
 
     // Check that it is possible to represent this enum:
-    let mut outer = true, did = local_def(id);
+    let mut outer = true;
+    let did = local_def(id);
     if ty::type_structurally_contains(ccx.tcx, rty, |sty| {
         match *sty {
           ty::ty_enum(id, _) if id == did => {
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 39715f295ad..ad173c69560 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -11,7 +11,7 @@
 use core::prelude::*;
 
 use middle::resolve::Impl;
-use middle::ty::{param_ty};
+use middle::ty::param_ty;
 use middle::ty;
 use middle::typeck::check::{FnCtxt, impl_self_ty};
 use middle::typeck::check::{structurally_resolved_type};
diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs
index 62320a1d34d..869825e607e 100644
--- a/src/librustc/middle/typeck/rscope.rs
+++ b/src/librustc/middle/typeck/rscope.rs
@@ -158,7 +158,7 @@ impl MethodRscope {
                variance: Option<ty::region_variance>,
                rcvr_generics: &ast::Generics)
             -> MethodRscope {
-        let mut region_param_names =
+        let region_param_names =
             RegionParamNames::from_generics(rcvr_generics);
         MethodRscope {
             self_ty: self_ty,
diff --git a/src/libstd/bitv.rs b/src/libstd/bitv.rs
index d89ce4232b1..d1f6bf982a7 100644
--- a/src/libstd/bitv.rs
+++ b/src/libstd/bitv.rs
@@ -891,7 +891,7 @@ mod tests {
     #[test]
     fn test_0_elements() {
         let mut act;
-        let mut exp;
+        let exp;
         act = Bitv::new(0u, false);
         exp = vec::from_elem::<uint>(0u, 0u);
         assert!(act.eq_vec(exp));
diff --git a/src/libstd/deque.rs b/src/libstd/deque.rs
index a88d13fda66..b120e40ec25 100644
--- a/src/libstd/deque.rs
+++ b/src/libstd/deque.rs
@@ -113,7 +113,7 @@ pub impl<T> Deque<T> {
     ///
     /// Fails if the deque is empty
     fn pop_front(&mut self) -> T {
-        let mut result = self.elts[self.lo].swap_unwrap();
+        let result = self.elts[self.lo].swap_unwrap();
         self.lo = (self.lo + 1u) % self.elts.len();
         self.nelts -= 1u;
         result
@@ -126,7 +126,7 @@ pub impl<T> Deque<T> {
         if self.hi == 0u {
             self.hi = self.elts.len() - 1u;
         } else { self.hi -= 1u; }
-        let mut result = self.elts[self.hi].swap_unwrap();
+        let result = self.elts[self.hi].swap_unwrap();
         self.elts[self.hi] = None;
         self.nelts -= 1u;
         result
@@ -204,7 +204,7 @@ pub impl<T> Deque<T> {
     ///
     /// Fails if the deque is empty
     fn pop_front(&mut self) -> T {
-        let mut result = self.elts[self.lo].swap_unwrap();
+        let result = self.elts[self.lo].swap_unwrap();
         self.lo = (self.lo + 1u) % self.elts.len();
         self.nelts -= 1u;
         result
@@ -217,7 +217,7 @@ pub impl<T> Deque<T> {
         if self.hi == 0u {
             self.hi = self.elts.len() - 1u;
         } else { self.hi -= 1u; }
-        let mut result = self.elts[self.hi].swap_unwrap();
+        let result = self.elts[self.hi].swap_unwrap();
         self.elts[self.hi] = None;
         self.nelts -= 1u;
         result
diff --git a/src/libstd/dlist.rs b/src/libstd/dlist.rs
index f9de2e0f58a..e736273a5ee 100644
--- a/src/libstd/dlist.rs
+++ b/src/libstd/dlist.rs
@@ -220,7 +220,7 @@ pub impl<T> DList<T> {
      * node. O(1).
      */
     fn push_head_n(@mut self, data: T) -> @mut DListNode<T> {
-        let mut nobe = DList::new_link(data);
+        let nobe = DList::new_link(data);
         self.add_head(nobe);
         nobe.get()
     }
@@ -233,7 +233,7 @@ pub impl<T> DList<T> {
      * node. O(1).
      */
     fn push_n(@mut self, data: T) -> @mut DListNode<T> {
-        let mut nobe = DList::new_link(data);
+        let nobe = DList::new_link(data);
         self.add_tail(nobe);
         nobe.get()
     }
@@ -263,7 +263,7 @@ pub impl<T> DList<T> {
         data: T,
         neighbour: @mut DListNode<T>
     ) -> @mut DListNode<T> {
-        let mut nobe = DList::new_link(data);
+        let nobe = DList::new_link(data);
         self.insert_left(nobe, neighbour);
         nobe.get()
     }
@@ -293,7 +293,7 @@ pub impl<T> DList<T> {
         data: T,
         neighbour: @mut DListNode<T>
     ) -> @mut DListNode<T> {
-        let mut nobe = DList::new_link(data);
+        let nobe = DList::new_link(data);
         self.insert_right(neighbour, nobe);
         nobe.get()
     }
diff --git a/src/libstd/num/rational.rs b/src/libstd/num/rational.rs
index ee6879e31d9..36652380bff 100644
--- a/src/libstd/num/rational.rs
+++ b/src/libstd/num/rational.rs
@@ -60,7 +60,7 @@ impl<T: Copy + Num + Ord>
 
     /// Put self into lowest terms, with denom > 0.
     fn reduce(&mut self) {
-        let mut g : T = gcd(self.numer, self.denom);
+        let g : T = gcd(self.numer, self.denom);
 
         self.numer /= g;
         self.denom /= g;
@@ -505,4 +505,4 @@ mod test {
             test(s);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs
index 48ecc0fc851..33bc393e470 100644
--- a/src/libstd/rope.rs
+++ b/src/libstd/rope.rs
@@ -838,8 +838,7 @@ pub mod node {
                   option::None => break,
                   option::Some(x) => {
                     //FIXME (#2744): Replace with memcpy or something similar
-                    let mut local_buf: ~[u8] =
-                        cast::transmute(*x.content);
+                    let local_buf: ~[u8] = cast::transmute(*x.content);
                     let mut i = x.byte_offset;
                     while i < x.byte_len {
                         buf[offset] = local_buf[i];
@@ -1156,7 +1155,7 @@ pub mod node {
         }
 
         pub fn empty() -> T {
-            let mut stack : ~[@Node] = ~[];
+            let stack : ~[@Node] = ~[];
             T { stack: stack, stackpos: -1 }
         }
 
diff --git a/src/libstd/sha1.rs b/src/libstd/sha1.rs
index 6aa4d1c54bc..7371250b38a 100644
--- a/src/libstd/sha1.rs
+++ b/src/libstd/sha1.rs
@@ -93,7 +93,7 @@ pub fn sha1() -> @Sha1 {
         assert!((vec::len(st.h) == digest_buf_len));
         assert!((vec::uniq_len(st.work_buf) == work_buf_len));
         let mut t: int; // Loop counter
-        let mut w = st.work_buf;
+        let w = st.work_buf;
 
         // Initialize the first 16 words of the vector w
         t = 0;
@@ -260,7 +260,7 @@ pub fn sha1() -> @Sha1 {
             return s;
         }
     }
-    let mut st = Sha1State {
+    let st = Sha1State {
          h: vec::from_elem(digest_buf_len, 0u32),
          len_low: 0u32,
          len_high: 0u32,
diff --git a/src/libstd/sort.rs b/src/libstd/sort.rs
index 72a888fcc91..febaea637ef 100644
--- a/src/libstd/sort.rs
+++ b/src/libstd/sort.rs
@@ -239,7 +239,7 @@ fn binarysort<T:Copy + Ord>(array: &mut [T], start: uint) {
             }
         }
         assert!(left == right);
-        let mut n = start-left;
+        let n = start-left;
 
         copy_vec(array, left+1, array, left, n);
         array[left] = pivot;
@@ -416,7 +416,7 @@ impl<T:Copy + Ord> MergeState<T> {
     }
 
     fn merge_at(&mut self, n: uint, array: &mut [T]) {
-        let mut size = self.runs.len();
+        let size = self.runs.len();
         assert!(size >= 2);
         assert!(n == size-2 || n == size-3);
 
diff --git a/src/libstd/test.rs b/src/libstd/test.rs
index 4ccbf207170..addc1da6394 100644
--- a/src/libstd/test.rs
+++ b/src/libstd/test.rs
@@ -427,8 +427,7 @@ fn run_tests(opts: &TestOpts,
     let filtered_descs = filtered_tests.map(|t| t.desc);
     callback(TeFiltered(filtered_descs));
 
-    let mut (filtered_tests,
-             filtered_benchs) =
+    let (filtered_tests, filtered_benchs) =
         do vec::partition(filtered_tests) |e| {
         match e.testfn {
             StaticTestFn(_) | DynTestFn(_) => true,
diff --git a/src/libstd/time.rs b/src/libstd/time.rs
index 70dc4d8cfeb..a763aa1592e 100644
--- a/src/libstd/time.rs
+++ b/src/libstd/time.rs
@@ -147,7 +147,7 @@ pub fn empty_tm() -> Tm {
 /// Returns the specified time in UTC
 pub fn at_utc(clock: Timespec) -> Tm {
     unsafe {
-        let mut Timespec { sec, nsec } = clock;
+        let Timespec { sec, nsec } = clock;
         let mut tm = empty_tm();
         rustrt::rust_gmtime(sec, nsec, &mut tm);
         tm
@@ -162,7 +162,7 @@ pub fn now_utc() -> Tm {
 /// Returns the specified time in the local timezone
 pub fn at(clock: Timespec) -> Tm {
     unsafe {
-        let mut Timespec { sec, nsec } = clock;
+        let Timespec { sec, nsec } = clock;
         let mut tm = empty_tm();
         rustrt::rust_localtime(sec, nsec, &mut tm);
         tm
diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs
index 807371490db..c170ee5c119 100644
--- a/src/libsyntax/diagnostic.rs
+++ b/src/libsyntax/diagnostic.rs
@@ -342,7 +342,7 @@ fn highlight_lines_internal(cm: @codemap::CodeMap,
         while num > 0u { num /= 10u; digits += 1u; }
 
         // indent past |name:## | and the 0-offset column location
-        let mut left = str::len(fm.name) + digits + lo.col.to_uint() + 3u;
+        let left = str::len(fm.name) + digits + lo.col.to_uint() + 3u;
         let mut s = ~"";
         // Skip is the number of characters we need to skip because they are
         // part of the 'filename:line ' part of the previous line.
diff --git a/src/libsyntax/ext/fmt.rs b/src/libsyntax/ext/fmt.rs
index 3ebe844950a..6a877040f48 100644
--- a/src/libsyntax/ext/fmt.rs
+++ b/src/libsyntax/ext/fmt.rs
@@ -96,7 +96,7 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
             }
         }
         fn make_ty(cx: @ext_ctxt, sp: span, t: Ty) -> @ast::expr {
-            let mut rt_type;
+            let rt_type;
             match t {
               TyHex(c) => match c {
                 CaseUpper => rt_type = ~"TyHexUpper",
@@ -272,6 +272,7 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
     /* Translate each piece (portion of the fmt expression) by invoking the
        corresponding function in core::unstable::extfmt. Each function takes a
        buffer to insert data into along with the data being formatted. */
+    let npieces = pieces.len();
     do vec::consume(pieces) |i, pc| {
         match pc {
             /* Raw strings get appended via str::push_str */
@@ -279,9 +280,10 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
                 let portion = mk_uniq_str(cx, fmt_sp, s);
 
                 /* If this is the first portion, then initialize the local
-                   buffer with it directly */
+                   buffer with it directly. If it's actually the only piece,
+                   then there's no need for it to be mutable */
                 if i == 0 {
-                    stms.push(mk_local(cx, fmt_sp, true, ident, portion));
+                    stms.push(mk_local(cx, fmt_sp, npieces > 1, ident, portion));
                 } else {
                     let args = ~[mk_mut_addr_of(cx, fmt_sp, buf()), portion];
                     let call = mk_call_global(cx,
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index bf8f03d4bf6..f851b9781ab 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -73,7 +73,7 @@ impl parser_attr for Parser {
         self.expect(&token::LBRACKET);
         let meta_item = self.parse_meta_item();
         self.expect(&token::RBRACKET);
-        let mut hi = self.span.hi;
+        let hi = self.span.hi;
         return spanned(lo, hi, ast::attribute_ { style: style,
                                                  value: meta_item,
                                                  is_sugared_doc: false });
@@ -141,16 +141,16 @@ impl parser_attr for Parser {
             token::EQ => {
                 self.bump();
                 let lit = self.parse_lit();
-                let mut hi = self.span.hi;
+                let hi = self.span.hi;
                 @spanned(lo, hi, ast::meta_name_value(name, lit))
             }
             token::LPAREN => {
                 let inner_items = self.parse_meta_seq();
-                let mut hi = self.span.hi;
+                let hi = self.span.hi;
                 @spanned(lo, hi, ast::meta_list(name, inner_items))
             }
             _ => {
-                let mut hi = self.span.hi;
+                let hi = self.span.hi;
                 @spanned(lo, hi, ast::meta_word(name))
             }
         }
diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs
index 2f6bfd4cfc5..b73544e95d6 100644
--- a/src/libsyntax/parse/comments.rs
+++ b/src/libsyntax/parse/comments.rs
@@ -229,7 +229,7 @@ fn read_block_comment(rdr: @mut StringReader,
     debug!(">>> block comment");
     let p = rdr.last_pos;
     let mut lines: ~[~str] = ~[];
-    let mut col: CharPos = rdr.col;
+    let col: CharPos = rdr.col;
     bump(rdr);
     bump(rdr);
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 42275aca0a3..21ea9f819b0 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -810,7 +810,7 @@ pub impl Parser {
     // This version of parse arg doesn't necessarily require
     // identifier names.
     fn parse_arg_general(&self, require_name: bool) -> arg {
-        let mut m;
+        let m;
         let mut is_mutbl = false;
         let pat = if require_name || self.is_named_argument() {
             m = self.parse_arg_mode();
@@ -1154,7 +1154,7 @@ pub impl Parser {
         let lo = self.span.lo;
         let mut hi = self.span.hi;
 
-        let mut ex: expr_;
+        let ex: expr_;
 
         if *self.token == token::LPAREN {
             self.bump();
@@ -1629,9 +1629,9 @@ pub impl Parser {
     // parse a prefix-operator expr
     fn parse_prefix_expr(&self) -> @expr {
         let lo = self.span.lo;
-        let mut hi;
+        let hi;
 
-        let mut ex;
+        let ex;
         match *self.token {
           token::NOT => {
             self.bump();
@@ -1781,7 +1781,7 @@ pub impl Parser {
           token::BINOPEQ(op) => {
               self.bump();
               let rhs = self.parse_expr();
-              let mut aop;
+              let aop;
               match op {
                   token::PLUS => aop = add,
                   token::MINUS => aop = subtract,
@@ -1956,7 +1956,7 @@ pub impl Parser {
         let lo = self.last_span.lo;
         let cond = self.parse_expr();
         let body = self.parse_block_no_value();
-        let mut hi = body.span.hi;
+        let hi = body.span.hi;
         return self.mk_expr(lo, hi, expr_while(cond, body));
     }
 
@@ -1984,7 +1984,7 @@ pub impl Parser {
 
             let lo = self.last_span.lo;
             let body = self.parse_block_no_value();
-            let mut hi = body.span.hi;
+            let hi = body.span.hi;
             return self.mk_expr(lo, hi, expr_loop(body, opt_ident));
         } else {
             // This is a 'continue' expression
@@ -2043,7 +2043,7 @@ pub impl Parser {
 
             arms.push(ast::arm { pats: pats, guard: guard, body: blk });
         }
-        let mut hi = self.span.hi;
+        let hi = self.span.hi;
         self.bump();
         return self.mk_expr(lo, hi, expr_match(discriminant, arms));
     }
@@ -2162,7 +2162,7 @@ pub impl Parser {
             let hi1 = self.last_span.lo;
             let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1),
                                                     fieldname);
-            let mut subpat;
+            let subpat;
             if *self.token == token::COLON {
                 self.bump();
                 subpat = self.parse_pat(refutable);
@@ -2183,7 +2183,7 @@ pub impl Parser {
 
         let lo = self.span.lo;
         let mut hi = self.span.hi;
-        let mut pat;
+        let pat;
         match *self.token {
           token::UNDERSCORE => { self.bump(); pat = pat_wild; }
           token::AT => {
@@ -2534,7 +2534,7 @@ pub impl Parser {
             match self.parse_item_or_view_item(/*bad*/ copy item_attrs,
                                                true, false, false) {
               iovi_item(i) => {
-                let mut hi = i.span.hi;
+                let hi = i.span.hi;
                 let decl = @spanned(lo, hi, decl_item(i));
                 return @spanned(lo, hi, stmt_decl(decl, self.get_id()));
               }
@@ -2704,7 +2704,7 @@ pub impl Parser {
                 }
             }
         }
-        let mut hi = self.span.hi;
+        let hi = self.span.hi;
         self.bump();
         let bloc = ast::blk_ {
             view_items: view_items,
@@ -3590,7 +3590,7 @@ pub impl Parser {
         let purity = self.parse_fn_purity();
         let (ident, generics) = self.parse_fn_header();
         let decl = self.parse_fn_decl(|p| p.parse_arg());
-        let mut hi = self.span.hi;
+        let hi = self.span.hi;
         self.expect(&token::SEMI);
         @ast::foreign_item { ident: ident,
                              attrs: attrs,
@@ -3798,7 +3798,7 @@ pub impl Parser {
             }
         }
         self.bump();
-        let mut actual_dtor = do the_dtor.map |dtor| {
+        let actual_dtor = do the_dtor.map |dtor| {
             let (d_body, d_attrs, d_s) = copy *dtor;
             codemap::spanned { node: ast::struct_dtor_ { id: self.get_id(),
                                                      attrs: d_attrs,
diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs
index eff7524e194..17add33d673 100644
--- a/src/libsyntax/print/pp.rs
+++ b/src/libsyntax/print/pp.rs
@@ -146,9 +146,9 @@ pub fn mk_printer(out: @io::Writer, linewidth: uint) -> @mut Printer {
     // fall behind.
     let n: uint = 3 * linewidth;
     debug!("mk_printer %u", linewidth);
-    let mut token: ~[token] = vec::from_elem(n, EOF);
-    let mut size: ~[int] = vec::from_elem(n, 0);
-    let mut scan_stack: ~[uint] = vec::from_elem(n, 0u);
+    let token: ~[token] = vec::from_elem(n, EOF);
+    let size: ~[int] = vec::from_elem(n, 0);
+    let scan_stack: ~[uint] = vec::from_elem(n, 0u);
     @mut Printer {
         out: @out,
         buf_len: n,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 8cf73599562..ce772ca7c35 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1972,7 +1972,7 @@ pub fn print_ty_fn(s: @ps,
 
 pub fn maybe_print_trailing_comment(s: @ps, span: codemap::span,
                                     next_pos: Option<BytePos>) {
-    let mut cm;
+    let cm;
     match s.cm { Some(ccm) => cm = ccm, _ => return }
     match next_comment(s) {
       Some(ref cmnt) => {
diff --git a/src/test/compile-fail/unused-mut-variables.rs b/src/test/compile-fail/unused-mut-variables.rs
new file mode 100644
index 00000000000..d1223cd8893
--- /dev/null
+++ b/src/test/compile-fail/unused-mut-variables.rs
@@ -0,0 +1,42 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Exercise the unused_mut attribute in some positive and negative cases
+
+#[allow(dead_assignment)];
+#[allow(unused_variable)];
+#[deny(unused_mut)];
+
+fn main() {
+    // negative cases
+    let mut a = 3; //~ ERROR: variable does not need to be mutable
+    let mut a = 2, b = 3; //~ ERROR: variable does not need to be mutable
+                          //~^ ERROR: variable does not need to be mutable
+    let mut a = ~[3]; //~ ERROR: variable does not need to be mutable
+
+    // positive cases
+    let mut a = 2;
+    a = 3;
+    let mut a = ~[];
+    a.push(3);
+    let mut a = ~[];
+    do callback {
+        a.push(3);
+    }
+}
+
+fn callback(f: &fn()) {}
+
+// make sure the lint attribute can be turned off
+#[allow(unused_mut)]
+fn foo(mut a: int) {
+    let mut a = 3;
+    let mut b = ~[2];
+}