use syntax::visit::Visitor;
use syntax::{ast, ast_util, visit};
-#[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
+#[deriving(Clone, Show, Eq, Ord, TotalEq, TotalOrd)]
pub enum Lint {
CTypes,
UnusedImports,
UnknownFeatures,
UnknownCrateType,
UnsignedNegate,
+ VariantSizeDifference,
ManagedHeapMemory,
OwnedHeapMemory,
pub type LintDict = HashMap<&'static str, LintSpec>;
+// this is public for the lints that run in trans
#[deriving(Eq)]
-enum LintSource {
+pub enum LintSource {
Node(Span),
Default,
CommandLine
default: Warn
}),
+ ("variant_size_difference",
+ LintSpec {
+ lint: VariantSizeDifference,
+ desc: "detects enums with widely varying variant sizes",
+ default: Allow,
+ }),
+
("unused_must_use",
LintSpec {
lint: UnusedMustUse,
/// Ids of structs/enums which have been checked for raw_pointer_deriving
checked_raw_pointers: NodeSet,
+
+ /// Level of EnumSizeVariance lint for each enum, stored here because the
+ /// body of the lint needs to run in trans.
+ enum_levels: HashMap<ast::NodeId, (Level, LintSource)>,
+}
+
+pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span,
+ lint_str: &str, tcx: &ty::ctxt) {
+ if level == Allow { return }
+
+ let mut note = None;
+ let msg = match src {
+ Default => {
+ format!("{}, \\#[{}({})] on by default", msg,
+ level_to_str(level), lint_str)
+ },
+ CommandLine => {
+ format!("{} [-{} {}]", msg,
+ match level {
+ Warn => 'W', Deny => 'D', Forbid => 'F',
+ Allow => fail!()
+ }, lint_str.replace("_", "-"))
+ },
+ Node(src) => {
+ note = Some(src);
+ msg.to_str()
+ }
+ };
+
+ match level {
+ Warn => { tcx.sess.span_warn(span, msg.as_slice()); }
+ Deny | Forbid => { tcx.sess.span_err(span, msg.as_slice()); }
+ Allow => fail!(),
+ }
+
+ for &span in note.iter() {
+ tcx.sess.span_note(span, "lint level defined here");
+ }
+}
+
+pub fn lint_to_str(lint: Lint) -> &'static str {
+ for &(name, lspec) in lint_table.iter() {
+ if lspec.lint == lint {
+ return name;
+ }
+ }
+
+ fail!("unrecognized lint: {}", lint);
}
impl<'a> Context<'a> {
return *k;
}
}
- fail!("unregistered lint {:?}", lint);
+ fail!("unregistered lint {}", lint);
}
fn span_lint(&self, lint: Lint, span: Span, msg: &str) {
Some(&(Warn, src)) => (self.get_level(Warnings), src),
Some(&pair) => pair,
};
- if level == Allow { return }
-
- let mut note = None;
- let msg = match src {
- Default => {
- format_strbuf!("{}, \\#[{}({})] on by default",
- msg,
- level_to_str(level),
- self.lint_to_str(lint))
- },
- CommandLine => {
- format!("{} [-{} {}]", msg,
- match level {
- Warn => 'W', Deny => 'D', Forbid => 'F',
- Allow => fail!()
- }, self.lint_to_str(lint).replace("_", "-"))
- },
- Node(src) => {
- note = Some(src);
- msg.to_str()
- }
- };
- match level {
- Warn => self.tcx.sess.span_warn(span, msg.as_slice()),
- Deny | Forbid => self.tcx.sess.span_err(span, msg.as_slice()),
- Allow => fail!(),
- }
- for &span in note.iter() {
- self.tcx.sess.span_note(span, "lint level defined here");
- }
+ emit_lint(level, src, msg, span, self.lint_to_str(lint), self.tcx);
}
/**
cx.span_lint(lint, e.span, msg.as_slice());
}
+fn check_enum_variant_sizes(cx: &mut Context, it: &ast::Item) {
+ match it.node {
+ ast::ItemEnum(..) => {
+ match cx.cur.find(&(VariantSizeDifference as uint)) {
+ Some(&(lvl, src)) if lvl != Allow => {
+ cx.node_levels.insert((it.id, VariantSizeDifference), (lvl, src));
+ },
+ _ => { }
+ }
+ },
+ _ => { }
+ }
+}
+
impl<'a> Visitor<()> for Context<'a> {
fn visit_item(&mut self, it: &ast::Item, _: ()) {
self.with_lint_attrs(it.attrs.as_slice(), |cx| {
+ check_enum_variant_sizes(cx, it);
check_item_ctypes(cx, it);
check_item_non_camel_case_types(cx, it);
check_item_non_uppercase_statics(cx, it);
lint_stack: Vec::new(),
negated_expr_id: -1,
checked_raw_pointers: NodeSet::new(),
+ enum_levels: HashMap::new(),
};
// Install default lint levels, followed by the command line levels, and
// in the iteration code.
for (id, v) in tcx.sess.lints.borrow().iter() {
for &(lint, span, ref msg) in v.iter() {
- tcx.sess.span_bug(span,
- format!("unprocessed lint {:?} at {}: {}",
- lint,
- tcx.map.node_to_str(*id),
- *msg).as_slice())
+ tcx.sess.span_bug(span, format!("unprocessed lint {} at {}: {}",
+ lint, tcx.map.node_to_str(*id), *msg).as_slice())
}
}
tcx.sess.abort_if_errors();
+ *tcx.enum_lint_levels.borrow_mut() = cx.enum_levels;
}
use lib::llvm::{llvm, Vector};
use lib;
use metadata::{csearch, encoder};
+use middle::lint;
use middle::astencode;
use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
use middle::weak_lang_items;
use middle::trans::glue;
use middle::trans::inline;
use middle::trans::machine;
-use middle::trans::machine::{llalign_of_min, llsize_of};
+use middle::trans::machine::{llalign_of_min, llsize_of, llsize_of_real};
use middle::trans::meth;
use middle::trans::monomorphize;
use middle::trans::tvec;
}
fn trans_enum_def(ccx: &CrateContext, enum_definition: &ast::EnumDef,
- id: ast::NodeId, vi: &[Rc<ty::VariantInfo>],
+ sp: Span, id: ast::NodeId, vi: &[Rc<ty::VariantInfo>],
i: &mut uint) {
for &variant in enum_definition.variants.iter() {
let disr_val = vi[*i].disr_val;
}
}
}
+
+ enum_variant_size_lint(ccx, enum_definition, sp, id);
+}
+
+fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) {
+ let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully
+
+ let (lvl, src) = ccx.tcx.node_lint_levels.borrow()
+ .find(&(id, lint::VariantSizeDifference))
+ .map_or((lint::Allow, lint::Default), |&(lvl,src)| (lvl, src));
+
+ if lvl != lint::Allow {
+ let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id));
+ match *avar {
+ adt::General(_, ref variants) => {
+ for var in variants.iter() {
+ let mut size = 0;
+ for field in var.fields.iter().skip(1) {
+ // skip the dicriminant
+ size += llsize_of_real(ccx, sizing_type_of(ccx, *field));
+ }
+ sizes.push(size);
+ }
+ },
+ _ => { /* its size is either constant or unimportant */ }
+ }
+
+ let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0),
+ |(l, s, li), (idx, &size)|
+ if size > l {
+ (size, l, idx)
+ } else if size > s {
+ (l, size, li)
+ } else {
+ (l, s, li)
+ }
+ );
+
+ // we only warn if the largest variant is at least thrice as large as
+ // the second-largest.
+ if largest > slargest * 3 && slargest > 0 {
+ lint::emit_lint(lvl, src,
+ format!("enum variant is more than three times larger \
+ ({} bytes) than the next largest (ignoring padding)",
+ largest).as_slice(),
+ sp, lint::lint_to_str(lint::VariantSizeDifference), ccx.tcx());
+
+ ccx.sess().span_note(enum_def.variants.get(largest_index).span,
+ "this variant is the largest");
+ }
+ }
}
pub struct TransItemVisitor<'a> {
if !generics.is_type_parameterized() {
let vi = ty::enum_variants(ccx.tcx(), local_def(item.id));
let mut i = 0;
- trans_enum_def(ccx, enum_definition, item.id, vi.as_slice(), &mut i);
+ trans_enum_def(ccx, enum_definition, item.span, item.id, vi.as_slice(), &mut i);
}
}
ast::ItemStatic(_, m, expr) => {