Add --lint option support
This commit is contained in:
parent
bce7ee5f6a
commit
4a167fe915
|
|
@ -43,6 +43,7 @@ program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("d
|
|||
program.option("-e, --enclose [arg[,...][:value[,...]]]", "Embed everything in a big function, with configurable argument(s) & value(s).");
|
||||
program.option("--ie8", "Support non-standard Internet Explorer 8.");
|
||||
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
|
||||
program.option("--lint [options]", "Display some scope warnings.", parse_js());
|
||||
program.option("--name-cache <file>", "File to hold mangled name mappings.");
|
||||
program.option("--rename", "Force symbol expansion.");
|
||||
program.option("--no-rename", "Disable symbol expansion.");
|
||||
|
|
@ -64,6 +65,7 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
|
|||
"compress",
|
||||
"enclose",
|
||||
"ie8",
|
||||
"lint",
|
||||
"mangle",
|
||||
"sourceMap",
|
||||
"toplevel",
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ function minify(files, options) {
|
|||
enclose: false,
|
||||
ie8: false,
|
||||
keep_fnames: false,
|
||||
lint: null,
|
||||
mangle: {},
|
||||
nameCache: null,
|
||||
output: {},
|
||||
|
|
@ -169,7 +170,10 @@ function minify(files, options) {
|
|||
if (timings) timings.compress = Date.now();
|
||||
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
|
||||
if (timings) timings.scope = Date.now();
|
||||
if (options.mangle) toplevel.figure_out_scope(options.mangle);
|
||||
if (options.mangle || options.lint) {
|
||||
toplevel.figure_out_scope(options.mangle);
|
||||
if (options.lint) toplevel.scope_warnings(options.lint);
|
||||
}
|
||||
if (timings) timings.mangle = Date.now();
|
||||
if (options.mangle) {
|
||||
toplevel.compute_char_frequency(options.mangle);
|
||||
|
|
|
|||
96
lib/scope.js
96
lib/scope.js
|
|
@ -367,6 +367,18 @@ AST_Symbol.DEFMETHOD("unreferenced", function() {
|
|||
return !this.definition().references.length && !this.scope.pinned();
|
||||
});
|
||||
|
||||
AST_Symbol.DEFMETHOD("undeclared", function() {
|
||||
return this.definition().undeclared;
|
||||
});
|
||||
|
||||
AST_LabelRef.DEFMETHOD("undeclared", function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
AST_Label.DEFMETHOD("undeclared", function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
AST_Symbol.DEFMETHOD("definition", function() {
|
||||
return this.thedef;
|
||||
});
|
||||
|
|
@ -597,3 +609,87 @@ var base54 = (function() {
|
|||
}
|
||||
return base54;
|
||||
})();
|
||||
|
||||
AST_Toplevel.DEFMETHOD("scope_warnings", function(options) {
|
||||
options = defaults(options, {
|
||||
assign_to_global : true,
|
||||
eval : true,
|
||||
func_arguments : true,
|
||||
nested_defuns : true,
|
||||
undeclared : false, // this makes a lot of noise
|
||||
unreferenced : true,
|
||||
});
|
||||
var tw = new TreeWalker(function(node) {
|
||||
if (options.undeclared
|
||||
&& node instanceof AST_SymbolRef
|
||||
&& node.undeclared()) {
|
||||
// XXX: this also warns about JS standard names,
|
||||
// i.e. Object, Array, parseInt etc. Should add a list of
|
||||
// exceptions.
|
||||
AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
|
||||
name: node.name,
|
||||
file: node.start.file,
|
||||
line: node.start.line,
|
||||
col: node.start.col
|
||||
});
|
||||
}
|
||||
if (options.assign_to_global) {
|
||||
var sym = null;
|
||||
if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef)
|
||||
sym = node.left;
|
||||
else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef)
|
||||
sym = node.init;
|
||||
if (sym
|
||||
&& (sym.undeclared()
|
||||
|| (sym.global() && sym.scope !== sym.definition().scope))) {
|
||||
AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
|
||||
msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
|
||||
name: sym.name,
|
||||
file: sym.start.file,
|
||||
line: sym.start.line,
|
||||
col: sym.start.col
|
||||
});
|
||||
}
|
||||
}
|
||||
if (options.eval
|
||||
&& node instanceof AST_SymbolRef
|
||||
&& node.undeclared()
|
||||
&& node.name == "eval") {
|
||||
AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
|
||||
}
|
||||
if (options.unreferenced
|
||||
&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
|
||||
&& !(node instanceof AST_SymbolCatch)
|
||||
&& node.unreferenced()) {
|
||||
AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
|
||||
type: node instanceof AST_Label ? "Label" : "Symbol",
|
||||
name: node.name,
|
||||
file: node.start.file,
|
||||
line: node.start.line,
|
||||
col: node.start.col
|
||||
});
|
||||
}
|
||||
if (options.func_arguments
|
||||
&& node instanceof AST_Lambda
|
||||
&& node.uses_arguments) {
|
||||
AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
|
||||
name: node.name ? node.name.name : "anonymous",
|
||||
file: node.start.file,
|
||||
line: node.start.line,
|
||||
col: node.start.col
|
||||
});
|
||||
}
|
||||
if (options.nested_defuns
|
||||
&& node instanceof AST_Defun
|
||||
&& !(tw.parent() instanceof AST_Scope)) {
|
||||
AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
|
||||
name: node.name.name,
|
||||
type: tw.parent().TYPE,
|
||||
file: node.start.file,
|
||||
line: node.start.line,
|
||||
col: node.start.col
|
||||
});
|
||||
}
|
||||
});
|
||||
this.walk(tw);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user