Add shallow syntax checking
`bash -n` will do a shallow syntax check on a bash script, just checking that the script parses. It's possible to have scripts that pass all bashate style checks, but don't actually run because of a syntax error, so this adds error code E040 and reports the syntax errors that `bash -n` outputs. Change-Id: Ib128c54493221e71cca19a497a6efc4f5fc368c1
This commit is contained in:
parent
1d949d6e3e
commit
77da51f026
@ -46,6 +46,7 @@ Obsolete, deprecated or unsafe syntax
|
|||||||
Rules to identify obsolete, deprecated or unsafe syntax that should
|
Rules to identify obsolete, deprecated or unsafe syntax that should
|
||||||
not be used
|
not be used
|
||||||
|
|
||||||
|
- E040: Syntax errors reported by `bash -n`
|
||||||
- E041: Usage of $[ for arithmetic is deprecated for $((
|
- E041: Usage of $[ for arithmetic is deprecated for $((
|
||||||
- E042: local declaration hides errors
|
- E042: local declaration hides errors
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ from __future__ import absolute_import
|
|||||||
import argparse
|
import argparse
|
||||||
import fileinput
|
import fileinput
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from bashate import messages
|
from bashate import messages
|
||||||
@ -111,6 +112,25 @@ def check_hashbang(line, filename, report):
|
|||||||
report.print_error(MESSAGES['E005'].msg, line)
|
report.print_error(MESSAGES['E005'].msg, line)
|
||||||
|
|
||||||
|
|
||||||
|
def check_syntax(filename, report):
|
||||||
|
# get bash to check the syntax, parse the output for line numbers
|
||||||
|
# and syntax errors to send to the report.
|
||||||
|
syntax_pattern = re.compile('^.*?: line ([0-9]+): (.*)$')
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
['bash', '-n', filename], stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
outputs = proc.communicate()
|
||||||
|
if proc.returncode != 0:
|
||||||
|
syntax_errors = [
|
||||||
|
line for line in outputs[1].split('\n') if 'syntax error' in line]
|
||||||
|
for line in syntax_errors:
|
||||||
|
groups = syntax_pattern.match(line).groups()
|
||||||
|
error_message = groups[1]
|
||||||
|
lineno = int(groups[0])
|
||||||
|
msg = '%s: %s' % (MESSAGES['E040'].msg, error_message)
|
||||||
|
report.print_error(msg, filename=filename, filelineno=lineno)
|
||||||
|
|
||||||
|
|
||||||
class BashateRun(object):
|
class BashateRun(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -143,7 +163,7 @@ class BashateRun(object):
|
|||||||
return True
|
return True
|
||||||
return self.warning_list and re.search(self.warning_list, error)
|
return self.warning_list and re.search(self.warning_list, error)
|
||||||
|
|
||||||
def print_error(self, error, line,
|
def print_error(self, error, line='',
|
||||||
filename=None, filelineno=None):
|
filename=None, filelineno=None):
|
||||||
if self.should_ignore(error):
|
if self.should_ignore(error):
|
||||||
return
|
return
|
||||||
@ -183,6 +203,10 @@ class BashateRun(object):
|
|||||||
report = self
|
report = self
|
||||||
|
|
||||||
for fname in files:
|
for fname in files:
|
||||||
|
# simple syntax checking, as files can pass style but still cause
|
||||||
|
# syntax errors when you try to run them.
|
||||||
|
check_syntax(fname, report)
|
||||||
|
|
||||||
for line in fileinput.input(fname):
|
for line in fileinput.input(fname):
|
||||||
if fileinput.isfirstline():
|
if fileinput.isfirstline():
|
||||||
# if in_multiline when the new file starts then we didn't
|
# if in_multiline when the new file starts then we didn't
|
||||||
|
@ -145,6 +145,15 @@ _messages = {
|
|||||||
""",
|
""",
|
||||||
'default': 'E'
|
'default': 'E'
|
||||||
},
|
},
|
||||||
|
'E040': {
|
||||||
|
'msg': 'Syntax error',
|
||||||
|
'long_msg':
|
||||||
|
"""
|
||||||
|
`bash -n` determined that there was a syntax error preventing
|
||||||
|
the script from parsing correctly and running.
|
||||||
|
""",
|
||||||
|
'default': 'E'
|
||||||
|
},
|
||||||
'E041': {
|
'E041': {
|
||||||
'msg': 'Arithmetic expansion using $[ is deprecated for $((',
|
'msg': 'Arithmetic expansion using $[ is deprecated for $((',
|
||||||
'long_msg':
|
'long_msg':
|
||||||
|
7
bashate/tests/samples/E040_syntax_error.sh
Normal file
7
bashate/tests/samples/E040_syntax_error.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function myfn {
|
||||||
|
if [ '' == '' ]; then
|
||||||
|
echo 'Things'
|
||||||
|
fii
|
||||||
|
}
|
@ -224,6 +224,13 @@ class TestBashateSamples(base.TestCase):
|
|||||||
|
|
||||||
self.assert_error_found('E005', 1)
|
self.assert_error_found('E005', 1)
|
||||||
|
|
||||||
|
def test_sample_E040(self):
|
||||||
|
test_files = ['bashate/tests/samples/E040_syntax_error.sh']
|
||||||
|
self.run.register_errors('E040')
|
||||||
|
self.run.check_files(test_files, False)
|
||||||
|
|
||||||
|
self.assert_error_found('E040', 7)
|
||||||
|
|
||||||
def test_sample_warning(self):
|
def test_sample_warning(self):
|
||||||
# reuse a couple of the above files to make sure we turn
|
# reuse a couple of the above files to make sure we turn
|
||||||
# errors down to warnings if requested
|
# errors down to warnings if requested
|
||||||
|
Loading…
x
Reference in New Issue
Block a user