diff options
Diffstat (limited to 'tools/eslint/lib/rules/no-undef.js')
-rw-r--r-- | tools/eslint/lib/rules/no-undef.js | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/tools/eslint/lib/rules/no-undef.js b/tools/eslint/lib/rules/no-undef.js new file mode 100644 index 0000000000..5ee07f3124 --- /dev/null +++ b/tools/eslint/lib/rules/no-undef.js @@ -0,0 +1,90 @@ +/** + * @fileoverview Rule to flag references to undeclared variables. + * @author Mark Macdonald + * @copyright 2015 Nicholas C. Zakas. All rights reserved. + * @copyright 2013 Mark Macdonald. All rights reserved. + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +// none! + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +function isImplicitGlobal(variable) { + return variable.defs.every(function(def) { + return def.type === "ImplicitGlobalVariable"; + }); +} + +/** + * Gets the declared variable, defined in `scope`, that `ref` refers to. + * @param {Scope} scope The scope in which to search. + * @param {Reference} ref The reference to find in the scope. + * @returns {Variable} The variable, or null if ref refers to an undeclared variable. + */ +function getDeclaredGlobalVariable(scope, ref) { + var declaredGlobal = null; + scope.variables.some(function(variable) { + if (variable.name === ref.identifier.name) { + // If it's an implicit global, it must have a `writeable` field (indicating it was declared) + if (!isImplicitGlobal(variable) || {}.hasOwnProperty.call(variable, "writeable")) { + declaredGlobal = variable; + return true; + } + } + return false; + }); + return declaredGlobal; +} + +/** + * Checks if the given node is the argument of a typeof operator. + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} Whether or not the node is the argument of a typeof operator. + */ +function hasTypeOfOperator(node) { + var parent = node.parent; + return parent.type === "UnaryExpression" && parent.operator === "typeof"; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = function(context) { + + var NOT_DEFINED_MESSAGE = "\"{{name}}\" is not defined.", + READ_ONLY_MESSAGE = "\"{{name}}\" is read only."; + + return { + + "Program:exit": function(/*node*/) { + + var globalScope = context.getScope(); + + globalScope.through.forEach(function(ref) { + var variable = getDeclaredGlobalVariable(globalScope, ref), + name = ref.identifier.name; + + if (hasTypeOfOperator(ref.identifier)) { + return; + } + + if (!variable) { + context.report(ref.identifier, NOT_DEFINED_MESSAGE, { name: name }); + } else if (ref.isWrite() && variable.writeable === false) { + context.report(ref.identifier, READ_ONLY_MESSAGE, { name: name }); + } + }); + + } + + }; + +}; |