* Style checker for the Haiku C++ coding guidelines
* Added an html report output with highlights and tooltips * Still a few false positives and rules missing. Some regex could be reworked * TODO: ignore some rules when in comments, multifile support git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33856 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
#
|
||||
# Copyright 2009, Alexandre Deckner, [email protected]
|
||||
# Distributed under the terms of the MIT License.
|
||||
#
|
||||
import re, sys
|
||||
from utils import *
|
||||
|
||||
|
||||
def processMatches(matches, name, text, highlights):
|
||||
for match in matches:
|
||||
printMatch(name, match, text)
|
||||
highlights.append((match.start(), match.end(), name))
|
||||
|
||||
|
||||
def run(sourceFile, rules):
|
||||
file = open(sourceFile, 'r')
|
||||
text = file.read()
|
||||
|
||||
highlights = []
|
||||
|
||||
for name, regexp in rules.items():
|
||||
processMatches(regexp.finditer(text), name, text, highlights)
|
||||
|
||||
highlights.sort()
|
||||
highlights = checkHighlights(highlights)
|
||||
|
||||
file.close()
|
||||
renderHtml(text, highlights, sourceFile, "styleviolations.html")
|
||||
|
||||
|
||||
cppRules = {}
|
||||
cppRules["Line over 80 char"] = re.compile('[^\n]{81,}')
|
||||
cppRules["Spaces instead of tabs"] = re.compile(' ')
|
||||
cppRules["Missing space after for/if/select/while"] = re.compile('(for|if|select|while)\(')
|
||||
cppRules["Missing space at comment start"] = re.compile('//[a-zA-Z0-9]')
|
||||
cppRules["Missing space after operator"] \
|
||||
= re.compile('[a-zA-Z0-9](==|[,=>/+\-*;\|])[a-zA-Z0-9]')
|
||||
cppRules["Operator at line end"] = re.compile('([*=/+\-\|\&\?]|\&&|\|\|)\n')
|
||||
cppRules["Missing space"] = re.compile('\){')
|
||||
cppRules["Mixed tabs/spaces"] = re.compile('( \t]|\t )+')
|
||||
cppRules["Malformed else"] = re.compile('}[ \t]*\n[ \t]*else')
|
||||
cppRules["Lines between functions > 2"] = re.compile('\n}([ \t]*\n){4,}')
|
||||
cppRules["Lines between functions < 2"] = re.compile('\n}([ \t]*\n){0,2}.')
|
||||
|
||||
# TODO: ignore some rules in comments
|
||||
#cppRules["-Comment 1"] = re.compile('[^/]/\*(.|[\r\n])*?\*/')
|
||||
#cppRules["-Comment 2"] = re.compile('(//)[^\n]*')
|
||||
|
||||
|
||||
if len(sys.argv) == 2 and sys.argv[1] != "--help":
|
||||
run(sys.argv[1], cppRules)
|
||||
else:
|
||||
print "Usage: python checkstyle.py file.cpp\n"
|
||||
print "Checks a c++ source file against the Haiku Coding Guidelines."
|
||||
print "Outputs an highlighted html report in the styleviolations.html file.\n"
|
||||
@@ -0,0 +1,165 @@
|
||||
#
|
||||
# Copyright 2009, Alexandre Deckner, [email protected]
|
||||
# Distributed under the terms of the MIT License.
|
||||
#
|
||||
from cgi import escape
|
||||
|
||||
|
||||
# prints match to stdout
|
||||
def printMatch(name, match, source):
|
||||
start = match.start()
|
||||
end = match.end()
|
||||
startLine = source.count('\n', 0, start)
|
||||
startColumn = start - source.rfind('\n', 0, start)
|
||||
print name + " (line " + str(startLine + 1) + ", " + str(startColumn) \
|
||||
+ "): '" + match.group().replace('\n','\\n') + "'"
|
||||
|
||||
|
||||
# render in html, the file style.css is embedded
|
||||
def renderHtml(text, highlights, sourceFileName, outputFileName):
|
||||
splittedText = highlightSplit(text, highlights)
|
||||
|
||||
#styleFile = open("style.css")
|
||||
#style = styleFile.read()
|
||||
#styleFile.close()
|
||||
|
||||
file = open(outputFileName, 'w')
|
||||
file.write("""
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Style violations in """ + sourceFileName.split('/')[-1] \
|
||||
+ """</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<style type="text/css">""" + cssStyle() + """</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>""" + sourceFileName + """</p>
|
||||
<pre class="code">""")
|
||||
|
||||
temp = ""
|
||||
count = 0
|
||||
for slice in splittedText:
|
||||
if count % 2 == 0:
|
||||
temp += escape(slice) + '<highlight class="tooltip">'
|
||||
else:
|
||||
temp += escape(slice) + "<em>" + highlights[(count - 1) / 2][2] \
|
||||
+ "</em></highlight>"
|
||||
count += 1
|
||||
|
||||
temp += "</highlight>" # close the superfluous last highlight
|
||||
|
||||
count = 1
|
||||
for line in temp.split('\n'):
|
||||
file.write(str(count).rjust(4) + ' |' + line + '<br>')
|
||||
count += 1
|
||||
|
||||
file.write("""
|
||||
</pre>
|
||||
</body>
|
||||
</html>""")
|
||||
|
||||
file.close()
|
||||
|
||||
|
||||
# highlight overlap check
|
||||
def highlightOverlaps(highlight1, highlight2):
|
||||
#print "hl1", highlight1, "hl2", highlight2
|
||||
return not(highlight2[0] > highlight1[1] or highlight1[0] > highlight2[1])
|
||||
|
||||
|
||||
# splits the string in three parts before, between and after the highlight
|
||||
def splitByHighlight(string, highlight):
|
||||
return (string[:highlight[0]], string[highlight[0]:highlight[1]], \
|
||||
string[highlight[1]:])
|
||||
|
||||
|
||||
# splits the source text on highlights boundaries so that we can escape to html
|
||||
# without the need to recalculate the highlights positions
|
||||
def highlightSplit(string, highlights):
|
||||
splittedString = []
|
||||
text = string
|
||||
offset = 0
|
||||
lastEnd = 0
|
||||
for (start, end, name) in highlights:
|
||||
if start >= lastEnd:
|
||||
(before, between, after) = splitByHighlight( \
|
||||
text, (start - offset, end - offset))
|
||||
splittedString.append(before)
|
||||
splittedString.append(between)
|
||||
text = after
|
||||
lastEnd = end
|
||||
offset += len(before + between)
|
||||
else:
|
||||
print "overlap ", (start, end, name)
|
||||
splittedString.append(text)
|
||||
return splittedString
|
||||
|
||||
|
||||
# checkHighlights() checks for highlights overlaps
|
||||
def checkHighlights(highlights):
|
||||
highlights.sort()
|
||||
|
||||
index = 0
|
||||
lastHighlight = (-2, -1, '')
|
||||
|
||||
# merge overlapping highlights
|
||||
for highlight in highlights:
|
||||
if highlightOverlaps(highlight, lastHighlight):
|
||||
|
||||
newStart = min(lastHighlight[0], highlight[0])
|
||||
newEnd = max(lastHighlight[1], highlight[1])
|
||||
newComment = lastHighlight[2]
|
||||
|
||||
if (newComment.find(highlight[2]) == -1):
|
||||
newComment += " + " + highlight[2]
|
||||
|
||||
highlight = (newStart, newEnd, newComment)
|
||||
highlights[index] = highlight
|
||||
|
||||
# mark highlight to be deleted
|
||||
highlights[index - 1] = (0, 0, "")
|
||||
|
||||
lastHighlight = highlight
|
||||
index += 1
|
||||
|
||||
# remove "to be deleted" highlights
|
||||
return [ (start, end, comment) for (start, end, comment) in highlights \
|
||||
if (start, end, comment) != (0, 0, "") ]
|
||||
|
||||
|
||||
def cssStyle():
|
||||
return """
|
||||
highlight {
|
||||
background: #ffff00;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
div.code pre {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
highlight.tooltip em {
|
||||
display:none;
|
||||
}
|
||||
|
||||
highlight.tooltip:hover {
|
||||
border: 0;
|
||||
position: relative;
|
||||
z-index: 500;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
highlight.tooltip:hover em {
|
||||
font-style: normal;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: -10px;
|
||||
padding: 5px;
|
||||
color: #000;
|
||||
border: 1px solid #bbb;
|
||||
background: #ffc;
|
||||
width: auto;
|
||||
}"""
|
||||
Reference in New Issue
Block a user