function main( context ) {
	var throwStatements;
	var functionHash;
	var source;

	source = context.getSourceCode();
	functionHash = {};
	throwStatements = [];

	/**
	* Reports the error message.
	*
	* @private
	* @param {Object} fcn - function metadata
	* @param {string} type - error type
	*/
	function report( fcn, type ) {
		context.report({
			'loc': fcn.loc,
			'message': '`'+fcn.name+'` is missing `@throws {'+type+'}` annotation(s)'
		});
	}

	/**
	* Registers a function.
	*
	* @private
	* @param {ASTNode} node - function node
	*/
	function registerFunction( node ) {
		var annotations;
		if (
			node.id &&
			node.id.name &&
			!hasOwnProp( functionHash, node.id.name )
		) {
			annotations = extractThrowsAnnotations( node );
			functionHash[ node.id.name ] = {
				'name': node.id.name,
				'start': node.start,
				'end': node.end,
				'loc': node.loc,
				'throwAnnotations': annotations || [],
				'functionBoundaries': extractFunctionBoundaries( node ),
				'hasJSDoc': isArray( annotations )
			};
		}
	}

	/**
	* Checks whether a function node is assigned as a value to a `Object.defineProperty` expression.
	*
	* @private
	* @param {ASTNode} node - function node
	* @returns {(Array|null)} array of error annotation types attached to the object expression if applicable, `null` otherwise
	*/
	function checkDefineProperty( node ) {
		var callee;
		var parent;

		if ( node ) {
			parent = node.parent;
			if ( parent ) {
				parent = parent.parent;
			}
			if ( parent ) {
				parent = parent.parent;
			}
			if ( parent ) {
				callee = parent.callee;
				if (
					callee && callee.property &&
					callee.property.name === 'defineProperty'
				) {
					return extractThrowsAnnotations( parent.parent );
				}
				if ( callee && callee.name === 'defineProperty' ) {
					return extractThrowsAnnotations( parent.parent );
				}
			}
		}
		return null;
	}

	/**
	* Extracts `@throws` tags from JSDoc comment corresponding to the given node.
	*
	* @private
	* @param {ASTNode} node - function node
	* @returns {(Array|null)} array of error annotation types
	*/
	function extractThrowsAnnotations( node ) {
		var jsdoc;
		var tags;
		var ast;
		var out;
		var i;

		jsdoc = findJSDoc( source, node );
		if ( isObject( jsdoc ) ) {
			ast = parseJSDoc( jsdoc.value, DOPTS );
			tags = ast.tags;
			out = [];
			for ( i = 0; i < tags.length; i++ ) {
				if ( tags[ i ].title === 'throws' ) {
					out.push( tags[ i ].type.name );
				}
			}
		} else {
			out = checkDefineProperty( node );
		}
		return out;
	}

	/**
	* Finish processing of the current program and report any errors.
	*
	* @private
	* @returns {void}
	*/
	function finish() {
		var keys;
		var stmt;
		var fcn;
		var i;
		var j;

		keys = objectKeys( functionHash );
		for ( i = 0; i < keys.length; i++ ) {
			fcn = functionHash[ keys[ i ] ];
			if ( fcn.hasJSDoc ) {
				fcn.throwStatements = [];
				for ( j = 0; j < throwStatements.length; j++ ) {
					stmt = throwStatements[ j ];
					if ( isInFunction( stmt, fcn ) ) {
						fcn.throwStatements.push( stmt.type );
					}
				}
				validateFunction( fcn );
			}
		}
	}

	/**
	* Validates the given function and reports any errors.
	*
	* @private
	* @param {Object} fcn - function metadata
	*/
	function validateFunction( fcn ) {
		var annotations;
		var statements;
		var stmt;
		var i;

		if ( fcn.throwStatements.length > 0 ) {
			annotations = unique( fcn.throwAnnotations.sort() );
			statements = unique( fcn.throwStatements.sort() );
			for ( i = 0; i < statements.length; i++ ) {
				stmt = statements[ i ];
				if ( !contains( annotations, stmt ) ) {
					report( fcn, stmt );
				}
			}
		}
	}

	/**
	* Registers throw statements encountered in the program.
	*
	* @private
	* @param {ASTNode} node - ThrowStatement node
	*/
	function registerThrowStatement( node ) {
		var callee;
		var obj;

		callee = node.argument.callee;
		if ( callee && callee.name ) {
			obj = {
				'start': node.start,
				'end': node.end,
				'type': callee.name
			};
			throwStatements.push( obj );
		}
	}

	return {
		'Program:exit': finish,
		':function': registerFunction,
		'ThrowStatement': registerThrowStatement
	};
}
Rule for validating that functions are not missing `@throws` tags.

@param {Object} context - ESLint context
@returns {Object} validators