Checking JavaScript Variables With Python

So I have OCD when it comes to JavaScript because it’s such a lenient language and it is hard to tell when you’re misusing variables. The editors that I use on a daily basis don’t tell me if (1) a variable has been declared but never used and (2) if a variable is being used without having been declared. I wrote a python script which attempts to check for these cases. There may be a bunch of bugs as I did not write a JavaScript language parser but instead a series of checks based on regex. Here is the code:

Edit: One of the commenter’s pointed out that one could use jslint and jshint to receive way better information about your JS code. Thank you for the recommendation!

#!/usr/bin/python

import re
import sys

def rmescape(inptstri):
	backflag = 0
	outpstri = ""
	
	for letritem in inptstri:
		if (letritem == '\\'):
			backflag = 1
		
		elif (backflag == 1):
			backflag = 0
		
		else:
			outpstri += letritem
	
	return outpstri

scptflag = 0
readlist = []

while (1):
	tempread = sys.stdin.readline()
	
	if (not tempread):
		break
	
	# clean the front and end of the string
	tempread = tempread.strip()
	
	if (re.match("^</script.*$", tempread)):
		scptflag = 0
	
	if (scptflag == 1):
		templist = tempread.split(";")
		
		for tempitem in templist:
			if (tempitem not in readlist):
				readlist.append(tempitem)
	
	if (re.match("^<script.*$", tempread)):
		scptflag = 1

varslist = []

for lineread in readlist:
	# remove any escapes
	lineread = rmescape(lineread)
	
	# remove any chars, strings, regex
	lineread = re.sub("\'[^\']*\'", "", lineread)
	lineread = re.sub("\"[^\"]*\"", "", lineread)
	lineread = re.sub("\/[^\/]+\/", "", lineread)
	
	# remove any comments
	lineread = re.sub("//.*", "", lineread)
	
	# remove any lists, arrays, dictionaries
	lineread = re.sub("\<[^\>]*\>", "", lineread)
	lineread = re.sub("\[[^\]]*\]", "", lineread)
	lineread = re.sub("\{[^\}]*\}", "", lineread)
	#lineread = re.sub("\([^\)]*\)", "", lineread)
	
	tempstri = ""
	
	regxobjc = re.match("^var[ ]+(.*)$", lineread)
	
	if (regxobjc):
		tempstri = regxobjc.group(1)
	
	regxobjc = re.match("^function[ ]+(.*)$", lineread)
	
	if (regxobjc):
		tempstri = regxobjc.group(1)
		tempstri = tempstri.replace("(", ",")
		tempstri = tempstri.replace(")", "")
	
	if (tempstri != ""):
		# remove any spaces, tabs
		tempstri = tempstri.replace(" ", "")
		tempstri = tempstri.replace("\t", "")
		
		# remove any variable assignment values
		tempstri = re.sub("=[^,]*", "", tempstri)
		
		# split the line to get all of the decalred variables
		templist = tempstri.split(",")
		
		for tempitem in templist:
			if (tempitem not in varslist):
				varslist.append(tempitem)

#print(varslist)

for varsread in varslist:
	usedflag = 0
	
	for lineread in readlist:
		# remove any spaces, tabs
		lineread = lineread.replace(" ", "")
		lineread = lineread.replace("\t", "")
		
		# skip variable assignment lines
		regxobjc = re.match("^.*" + varsread + "=[^=].*$", lineread)
		
		if (regxobjc):
			continue
		
		# get variable usage lines
		regxobjc = re.match("^.*" + varsread + ".*$", lineread)
		
		if (regxobjc):
			usedflag = 1
			break
	
	if (usedflag == 0):
		print("note: variable possibly unused: [%s]" % (varsread))

nameregx = "[A-Za-z][0-9A-Za-z\_]*"
templist = ["null", "true", "false", "document", "window", "Math"]

for tempitem in templist:
	varslist.append(tempitem)

for lineread in readlist:
	origline = lineread
	
	# replace any variable declaration prefix's
	lineread = re.sub("^var ", "", lineread)
	
	# replace any function declaration lines
	lineread = re.sub("^function .*", "", lineread)
	
	# replace any return statement lines
	lineread = re.sub("^return .*", "", lineread)
	
	# remove any spaces, tabs
	lineread = lineread.replace(" ", "")
	lineread = lineread.replace("\t", "")
	
	# remove any chars, strings, regex
	lineread = re.sub("\'[^\']*\'", "", lineread)
	lineread = re.sub("\"[^\"]*\"", "", lineread)
	lineread = re.sub("\/[^\/]+\/", "", lineread)
	
	# remove any comments
	lineread = re.sub("//.*", "", lineread)
	
	# replace any method calls (special replace char to prevent function replace)
	lineread = re.sub("\." + nameregx + "[=\(]*", "~", lineread)
	
	# replace any assignments, function calls
	lineread = re.sub(nameregx + "[=\(]", "", lineread)
	
	while (1):
		regxobjc = re.search("(" + nameregx + ")", lineread)
		
		if (not regxobjc):
			break
		
		namestri = regxobjc.group(1)
		
		if (namestri not in varslist):
			print("warn: variable possibly undeclared: [%s] on line [%s]" % (namestri, origline))
		
		lineread = lineread.replace(namestri, "")

One thought on “Checking JavaScript Variables With Python

Leave a comment