Merge ba12997ce1 into 9e19e63551
This commit is contained in:
commit
75cafb6e31
|
|
@ -112,6 +112,7 @@ a double dash to prevent input files being used as option arguments:
|
|||
By default UglifyJS will not try to be IE-proof.
|
||||
--keep-fnames Do not mangle/drop function names. Useful for
|
||||
code relying on Function.prototype.name.
|
||||
--lint Display some scope warnings.
|
||||
--name-cache <file> File to hold mangled name mappings.
|
||||
--self Build UglifyJS as a library (implies --wrap UglifyJS)
|
||||
--source-map [options] Enable source map/specify source map options:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
20
test/input/lint/input.js
Normal file
20
test/input/lint/input.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
function assignToGlobal() {
|
||||
x = 1;
|
||||
}
|
||||
|
||||
function eval() {
|
||||
eval("var x = 1");
|
||||
}
|
||||
|
||||
function funcArguments() {
|
||||
console.log("args: ", arguments);
|
||||
}
|
||||
|
||||
function nestedDefuns() {
|
||||
if (true) {
|
||||
function fn() { }
|
||||
}
|
||||
}
|
||||
|
||||
function unreferencedFnc() { }
|
||||
undeclaredFnc();
|
||||
|
|
@ -706,4 +706,49 @@ describe("bin/uglifyjs", function() {
|
|||
done();
|
||||
});
|
||||
});
|
||||
it("Should warn for default options with --lint", function(done) {
|
||||
var command = uglifyjscmd + " test/input/lint/input.js --lint";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, 'function assignToGlobal(){x=1}function eval(){eval("var x = 1")}' +
|
||||
'function funcArguments(){console.log("args: ",arguments)}function nestedDefuns(){if(true){function fn(){}}}function unreferencedFnc(){}undeclaredFnc();\n');
|
||||
assert.strictEqual(stderr, [
|
||||
'WARN: Accidental global?: x [test/input/lint/input.js:2,4]',
|
||||
'WARN: arguments used in function funcArguments [test/input/lint/input.js:9,0]',
|
||||
'WARN: Function fn declared in nested statement "BlockStatement" [test/input/lint/input.js:15,8]',
|
||||
'WARN: Symbol fn is declared but not referenced [test/input/lint/input.js:15,17]',
|
||||
''
|
||||
].join('\n'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should warn for all lint options", function(done) {
|
||||
var command = uglifyjscmd + " test/input/lint/input.js --lint undeclared=true";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, 'function assignToGlobal(){x=1}function eval(){eval("var x = 1")}function funcArguments()' +
|
||||
'{console.log("args: ",arguments)}function nestedDefuns(){if(true){function fn(){}}}function unreferencedFnc(){}undeclaredFnc();\n');
|
||||
assert.strictEqual(stderr, [
|
||||
'WARN: Accidental global?: x [test/input/lint/input.js:2,4]',
|
||||
'WARN: Undeclared symbol: x [test/input/lint/input.js:2,4]',
|
||||
'WARN: arguments used in function funcArguments [test/input/lint/input.js:9,0]',
|
||||
'WARN: Undeclared symbol: console [test/input/lint/input.js:10,4]',
|
||||
'WARN: Function fn declared in nested statement "BlockStatement" [test/input/lint/input.js:15,8]',
|
||||
'WARN: Symbol fn is declared but not referenced [test/input/lint/input.js:15,17]',
|
||||
'WARN: Undeclared symbol: undeclaredFnc [test/input/lint/input.js:20,0]',
|
||||
''
|
||||
].join('\n'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should work with --lint arg=value", function(done) {
|
||||
var command = uglifyjscmd + " test/input/lint/input.js --lint assign_to_global=false,eval=false,func_arguments=false,nested_defuns=false,undeclared=false,unreferenced=true";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, 'function assignToGlobal(){x=1}function eval(){eval("var x = 1")}function funcArguments()' +
|
||||
'{console.log("args: ",arguments)}function nestedDefuns(){if(true){function fn(){}}}function unreferencedFnc(){}undeclaredFnc();\n');
|
||||
assert.strictEqual(stderr, 'WARN: Symbol fn is declared but not referenced [test/input/lint/input.js:15,17]\n');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user