about summary refs log tree commit diff
path: root/src/rustc
diff options
context:
space:
mode:
authorMichael Sullivan <sully@msully.net>2012-06-04 16:07:54 -0700
committerMichael Sullivan <sully@msully.net>2012-06-04 16:08:07 -0700
commit5a4e53487faa94f3ccb966abf278d7ab5e22711a (patch)
treebab4e770e2ccd21f784ab92d3e22f7b1f06dbbfb /src/rustc
parent2d0e7cd272136f12f8c07cafe57df408d37ada6f (diff)
downloadrust-5a4e53487faa94f3ccb966abf278d7ab5e22711a.tar.gz
rust-5a4e53487faa94f3ccb966abf278d7ab5e22711a.zip
Heavily rework lint infrastructure. Split it into two passes: one that builds the table and one that does the checks. Build the table early and make session know about it fo reasy use.
Diffstat (limited to 'src/rustc')
-rw-r--r--src/rustc/driver/driver.rs13
-rw-r--r--src/rustc/driver/session.rs19
-rw-r--r--src/rustc/middle/lint.rs209
3 files changed, 131 insertions, 110 deletions
diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs
index ebdccf59c53..6d4f48595d2 100644
--- a/src/rustc/driver/driver.rs
+++ b/src/rustc/driver/driver.rs
@@ -157,6 +157,9 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
         time(time_passes, "core injection",
              bind front::core_inject::maybe_inject_libcore_ref(sess, crate));
 
+    time(time_passes, "building warning settings table",
+         bind lint::build_settings_crate(sess, crate));
+
     let ast_map =
         time(time_passes, "ast indexing",
              bind syntax::ast_map::map_crate(sess.diagnostic(), *crate));
@@ -204,10 +207,8 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
              bind middle::alias::check_crate(ty_cx, crate));
     time(time_passes, "kind checking",
          bind kind::check_crate(ty_cx, method_map, last_use_map, crate));
-
-    let _warning_settings =
-        time(time_passes, "lint checking",
-             bind lint::check_crate(ty_cx, crate, sess.opts.lint_opts));
+    time(time_passes, "lint checking",
+         bind lint::check_crate(ty_cx, crate));
 
     if upto == cu_no_trans { ret {crate: crate, tcx: some(ty_cx)}; }
     let outputs = option::get(outputs);
@@ -528,6 +529,7 @@ fn build_session_(
         sopts.maybe_sysroot,
         sopts.target_triple,
         sopts.addl_lib_search_paths);
+    let warning_settings = lint::mk_warning_settings();
     @{targ_cfg: target_cfg,
       opts: sopts,
       cstore: cstore,
@@ -544,7 +546,8 @@ fn build_session_(
       span_diagnostic: span_diagnostic_handler,
       filesearch: filesearch,
       mut building_library: false,
-      working_dir: os::getcwd()}
+      working_dir: os::getcwd(),
+      warning_settings: warning_settings}
 }
 
 fn parse_pretty(sess: session, &&name: str) -> pp_mode {
diff --git a/src/rustc/driver/session.rs b/src/rustc/driver/session.rs
index 362fa823460..97e2d0ec4db 100644
--- a/src/rustc/driver/session.rs
+++ b/src/rustc/driver/session.rs
@@ -9,6 +9,7 @@ import back::target_strs;
 import back::link;
 import middle::lint;
 
+
 enum os { os_win32, os_macos, os_linux, os_freebsd, }
 
 enum arch { arch_x86, arch_x86_64, arch_arm, }
@@ -82,7 +83,8 @@ type session = @{targ_cfg: @config,
                  span_diagnostic: diagnostic::span_handler,
                  filesearch: filesearch::filesearch,
                  mut building_library: bool,
-                 working_dir: str};
+                 working_dir: str,
+                 warning_settings: lint::warning_settings};
 
 impl session for session {
     fn span_fatal(sp: span, msg: str) -> ! {
@@ -127,6 +129,21 @@ impl session for session {
     fn unimpl(msg: str) -> ! {
         self.span_diagnostic.handler().unimpl(msg)
     }
+    fn span_lint_level(level: lint::level,
+                       sp: span, msg: str) {
+        alt level {
+          lint::ignore { }
+          lint::warn { self.span_warn(sp, msg); }
+          lint::error { self.span_err(sp, msg); }
+        }
+    }
+    fn span_lint(lint_mode: lint::lint,
+                 expr_id: ast::node_id, item_id: ast::node_id,
+                 span: span, msg: str) {
+        let level = lint::get_warning_settings_level(
+            self.warning_settings, lint_mode, expr_id, item_id);
+        self.span_lint_level(level, span, msg);
+    }
     fn next_node_id() -> ast::node_id {
         ret syntax::parse::next_node_id(self.parse_sess);
     }
diff --git a/src/rustc/middle/lint.rs b/src/rustc/middle/lint.rs
index 88444f9a0f1..dc31c75733e 100644
--- a/src/rustc/middle/lint.rs
+++ b/src/rustc/middle/lint.rs
@@ -1,3 +1,4 @@
+import driver::session;
 import driver::session::session;
 import middle::ty;
 import syntax::{ast, visit};
@@ -10,8 +11,9 @@ import syntax::print::pprust::expr_to_str;
 
 export lint, ctypes, unused_imports;
 export level, ignore, warn, error;
-export lookup_lint, lint_dict, get_lint_dict, check_crate;
-export warning_settings, warning_methods;
+export lookup_lint, lint_dict, get_lint_dict, get_warning_settings_level;
+export check_crate, build_settings_crate, mk_warning_settings;
+export warning_settings;
 
 #[doc="
 
@@ -29,6 +31,10 @@ omitted. If we start allowing warn attributes on expressions, we will start
 having entries for expressions that do not share their enclosing items
 settings.
 
+This module then, exports two passes: one that populates the warning settings
+table in the session and is run early in the compile process, and one that
+does a variety of lint checks, and is run late in the compile process.
+
 "]
 
 enum lint {
@@ -115,6 +121,11 @@ type warning_settings = {
     settings_map: lint_mode_map
 };
 
+fn mk_warning_settings() -> warning_settings {
+    {default_settings: std::smallintmap::mk(),
+     settings_map: int_hash()}
+}
+
 fn get_warning_level(modes: lint_modes, lint: lint) -> level {
     alt modes.find(lint as uint) {
       some(c) { c }
@@ -122,32 +133,16 @@ fn get_warning_level(modes: lint_modes, lint: lint) -> level {
     }
 }
 
-fn span_lint(tcx: ty::ctxt, level: level, span: span, msg: str) {
-    alt level {
-      ignore { }
-      warn { tcx.sess.span_warn(span, msg); }
-      error { tcx.sess.span_err(span, msg); }
+fn get_warning_settings_level(settings: warning_settings,
+                              lint_mode: lint,
+                              _expr_id: ast::node_id,
+                              item_id: ast::node_id) -> level {
+    alt settings.settings_map.find(item_id) {
+      some(modes) { get_warning_level(modes, lint_mode) }
+      none { get_warning_level(settings.default_settings, lint_mode) }
     }
 }
 
-impl warning_methods for warning_settings {
-    fn get_level(lint_mode: lint,
-                 _expr_id: ast::node_id, item_id: ast::node_id) -> level {
-        alt self.settings_map.find(item_id) {
-          some(modes) { get_warning_level(modes, lint_mode) }
-          none { get_warning_level(self.default_settings, lint_mode) }
-        }
-    }
-
-    fn span_lint(tcx: ty::ctxt, lint_mode: lint,
-                 expr_id: ast::node_id, item_id: ast::node_id,
-                 span: span, msg: str) {
-        let level = self.get_level(lint_mode, expr_id, item_id);
-        span_lint(tcx, level, span, msg);
-    }
-
-}
-
 // This is kind of unfortunate. It should be somewhere else, or we should use
 // a persistent data structure...
 fn clone_lint_modes(modes: lint_modes) -> lint_modes {
@@ -157,8 +152,7 @@ fn clone_lint_modes(modes: lint_modes) -> lint_modes {
 type ctxt = {dict: lint_dict,
              curr: lint_modes,
              is_default: bool,
-             lint_mode_map: lint_mode_map,
-             tcx: ty::ctxt};
+             sess: session};
 
 
 impl methods for ctxt {
@@ -175,7 +169,7 @@ impl methods for ctxt {
     }
 
     fn span_lint(level: level, span: span, msg: str) {
-        span_lint(self.tcx, level, span, msg);
+        self.sess.span_lint_level(level, span, msg);
     }
 
     #[doc="
@@ -213,7 +207,7 @@ impl methods for ctxt {
                         }
                       }
                       _ {
-                        self.tcx.sess.span_err(
+                        self.sess.span_err(
                             meta.span,
                             "malformed warning attribute");
                       }
@@ -221,8 +215,8 @@ impl methods for ctxt {
                 }
               }
               _ {
-                self.tcx.sess.span_err(meta.span,
-                                       "malformed warning attribute");
+                self.sess.span_err(meta.span,
+                                   "malformed warning attribute");
               }
             }
         }
@@ -249,25 +243,56 @@ fn lookup_lint(dict: lint_dict, s: str)
      })
 }
 
-fn check_item(i: @ast::item, &&cx: ctxt, v: visit::vt<ctxt>) {
+fn build_settings_item(i: @ast::item, &&cx: ctxt, v: visit::vt<ctxt>) {
     cx.with_warn_attrs(i.attrs) {|cx|
-        for cx.curr.each {|lint, level|
-            alt int_to_lint(lint as int) {
-              ctypes { check_item_ctypes(cx, level, i); }
-              unused_imports { check_item_unused_imports(cx, level, i); }
-              while_true { check_item_while_true(cx, level, i); }
-              path_statement { check_item_path_statement(cx, level, i); }
-              old_vecs { check_item_old_vecs(cx, level, i); }
-              unrecognized_warning { /* this is checked elsewhere */ }
-            }
-        }
         if !cx.is_default {
-            cx.lint_mode_map.insert(i.id, cx.curr);
+            cx.sess.warning_settings.settings_map.insert(i.id, cx.curr);
         }
         visit::visit_item(i, cx, v);
     }
 }
 
+fn build_settings_crate(sess: session::session, crate: @ast::crate) {
+
+    let cx = {dict: get_lint_dict(),
+              curr: std::smallintmap::mk(),
+              is_default: true,
+              sess: sess};
+
+    // Install defaults.
+    for cx.dict.each {|_k, spec| cx.set_level(spec.lint, spec.default); }
+
+    // Install command-line options, overriding defaults.
+    for sess.opts.lint_opts.each {|pair|
+        let (lint,level) = pair;
+        cx.set_level(lint, level);
+    }
+
+    cx.with_warn_attrs(crate.node.attrs) {|cx|
+        // Copy out the default settings
+        for cx.curr.each {|k, v|
+            sess.warning_settings.default_settings.insert(k, v);
+        }
+
+        let cx = {is_default: true with cx};
+
+        let visit = visit::mk_vt(@{
+            visit_item: build_settings_item
+            with *visit::default_visitor()
+        });
+        visit::visit_crate(*crate, cx, visit);
+    }
+
+    sess.abort_if_errors();
+}
+
+fn check_item(i: @ast::item, cx: ty::ctxt) {
+    check_item_ctypes(cx, i);
+    check_item_while_true(cx, i);
+    check_item_path_statement(cx, i);
+    check_item_old_vecs(cx, i);
+}
+
 // Take a visitor, and modify it so that it will not proceed past subitems.
 // This is used to make the simple visitors used for the lint passes
 // not traverse into subitems, since that is handled by the outer
@@ -276,16 +301,17 @@ fn item_stopping_visitor<E>(v: visit::vt<E>) -> visit::vt<E> {
     visit::mk_vt(@{visit_item: {|_i, _e, _v| } with **v})
 }
 
-fn check_item_while_true(cx: ctxt, level: level, it: @ast::item) {
+fn check_item_while_true(cx: ty::ctxt, it: @ast::item) {
     let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
         visit_expr: fn@(e: @ast::expr) {
            alt e.node {
              ast::expr_while(cond, _) {
                 alt cond.node {
                     ast::expr_lit(@{node: ast::lit_bool(true),_}) {
-                            cx.span_lint(
-                              level, e.span,
-                              "denote infinite loops with loop { ... }");
+                            cx.sess.span_lint(
+                                while_true, it.id, e.id,
+                                e.span,
+                                "denote infinite loops with loop { ... }");
                     }
                     _ {}
                 }
@@ -298,28 +324,26 @@ fn check_item_while_true(cx: ctxt, level: level, it: @ast::item) {
     visit::visit_item(it, (), visit);
 }
 
-fn check_item_unused_imports(_cx: ctxt, _level: level, _it: @ast::item) {
-    // FIXME: Don't know how to check this in lint yet, it's currently being
-    // done over in resolve. When resolve is rewritten, do it here instead.
-}
-
-fn check_item_ctypes(cx: ctxt, level: level, it: @ast::item) {
+fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
 
-    fn check_native_fn(cx: ctxt, level: level, decl: ast::fn_decl) {
+    fn check_native_fn(cx: ty::ctxt, fn_id: ast::node_id,
+                       decl: ast::fn_decl) {
         let tys = vec::map(decl.inputs) {|a| a.ty };
         for vec::each(tys + [decl.output]) {|ty|
             alt ty.node {
               ast::ty_path(_, id) {
-                alt cx.tcx.def_map.get(id) {
+                alt cx.def_map.get(id) {
                   ast::def_prim_ty(ast::ty_int(ast::ty_i)) {
-                    cx.span_lint(
-                        level, ty.span,
+                    cx.sess.span_lint(
+                        ctypes, fn_id, id,
+                        ty.span,
                         "found rust type `int` in native module, while \
                          libc::c_int or libc::c_long should be used");
                   }
                   ast::def_prim_ty(ast::ty_uint(ast::ty_u)) {
-                    cx.span_lint(
-                        level, ty.span,
+                    cx.sess.span_lint(
+                        ctypes, fn_id, id,
+                        ty.span,
                         "found rust type `uint` in native module, while \
                          libc::c_uint or libc::c_ulong should be used");
                   }
@@ -337,7 +361,7 @@ fn check_item_ctypes(cx: ctxt, level: level, it: @ast::item) {
         for nmod.items.each {|ni|
             alt ni.node {
               ast::native_item_fn(decl, tps) {
-                check_native_fn(cx, level, decl);
+                check_native_fn(cx, it.id, decl);
               }
             }
         }
@@ -346,15 +370,16 @@ fn check_item_ctypes(cx: ctxt, level: level, it: @ast::item) {
     }
 }
 
-fn check_item_path_statement(cx: ctxt, level: level, it: @ast::item) {
+fn check_item_path_statement(cx: ty::ctxt, it: @ast::item) {
     let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
         visit_stmt: fn@(s: @ast::stmt) {
             alt s.node {
-              ast::stmt_semi(@{id: _,
+              ast::stmt_semi(@{id: id,
                                node: ast::expr_path(@path),
                                span: _}, _) {
-                cx.span_lint(
-                    level, s.span,
+                cx.sess.span_lint(
+                    path_statement, it.id, id,
+                    s.span,
                     "path statement with no effect");
               }
               _ {}
@@ -365,7 +390,7 @@ fn check_item_path_statement(cx: ctxt, level: level, it: @ast::item) {
     visit::visit_item(it, (), visit);
 }
 
-fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) {
+fn check_item_old_vecs(cx: ty::ctxt, it: @ast::item) {
     let uses_vstore = int_hash();
 
     let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
@@ -375,7 +400,9 @@ fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) {
               ast::expr_vec(_, _) |
               ast::expr_lit(@{node: ast::lit_str(_), span:_})
               if ! uses_vstore.contains_key(e.id) {
-                cx.span_lint(level, e.span, "deprecated vec/str expr");
+                cx.sess.span_lint(
+                    old_vecs, it.id, e.id,
+                    e.span, "deprecated vec/str expr");
               }
               ast::expr_vstore(@inner, _) {
                 uses_vstore.insert(inner.id, true);
@@ -388,13 +415,17 @@ fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) {
             alt t.node {
               ast::ty_vec(_)
               if ! uses_vstore.contains_key(t.id) {
-                cx.span_lint(level, t.span, "deprecated vec type");
+                cx.sess.span_lint(
+                    old_vecs, it.id, t.id,
+                    t.span, "deprecated vec type");
               }
 
               ast::ty_path(@{span: _, global: _, idents: ids,
                              rp: none, types: _}, _)
               if ids == ["str"] && (! uses_vstore.contains_key(t.id)) {
-                cx.span_lint(level, t.span, "deprecated str type");
+                cx.sess.span_lint(
+                    old_vecs, it.id, t.id,
+                    t.span, "deprecated str type");
               }
 
               ast::ty_vstore(inner, _) {
@@ -409,45 +440,15 @@ fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) {
     visit::visit_item(it, (), visit);
 }
 
+fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
 
-fn check_crate(tcx: ty::ctxt, crate: @ast::crate,
-               lint_opts: [(lint, level)]) -> warning_settings {
-
-    fn hash_lint(&&lint: lint) -> uint { lint as uint }
-    fn eq_lint(&&a: lint, &&b: lint) -> bool { a == b }
-
-    let cx = {dict: get_lint_dict(),
-              curr: std::smallintmap::mk(),
-              is_default: true,
-              lint_mode_map: int_hash(),
-              tcx: tcx};
-
-    let mut default_settings = cx.curr; // dummy value
-
-    // Install defaults.
-    for cx.dict.each {|_k, spec| cx.set_level(spec.lint, spec.default); }
-
-    // Install command-line options, overriding defaults.
-    for lint_opts.each {|pair|
-        let (lint,level) = pair;
-        cx.set_level(lint, level);
-    }
-
-    cx.with_warn_attrs(crate.node.attrs) {|cx|
-        default_settings = cx.curr;
-        let cx = {is_default: true with cx};
-
-        let visit = visit::mk_vt(@{
-            visit_item: check_item
-            with *visit::default_visitor()
-        });
-        visit::visit_crate(*crate, cx, visit);
-    }
+    let v = visit::mk_simple_visitor(@{
+        visit_item: fn@(it: @ast::item) { check_item(it, tcx); }
+        with *visit::default_simple_visitor()
+    });
+    visit::visit_crate(*crate, (), v);
 
     tcx.sess.abort_if_errors();
-
-    ret {default_settings: default_settings,
-         settings_map: cx.lint_mode_map};
 }
 
 //