📄 sainity
字号:
@desc = "Using variations of (x ? true : false) or (x ? false : true) is just plain stupid." end def parse_file(file, lines) results = lines.select do |line| line.text =~ /\?\s*(true|false)\s*:\s*(true|false)/i end add_results( file, results ) endendclass LoopOnConstant < SainityCheck def initialize super @name = "LoopingOnConstant" @title = "Looping On Constant" @type = "Garbage" @desc = "This checks detects loops that evaluate constant values " @desc += "(true,false,0..) or \"for\" loops with no conditionals. all " @desc += "are often a sign of poor code and in most cases can be avoided " @desc += "or replaced with more elegant code." end def parse_file(file, lines) results = lines.select do |line| line.text =~ /while\s*\(\s*([0-9]+|true|false)\s*\)/i or line.text =~ /for\s*\([^;]*;\s*;[^;]*\)/ end add_results( file, results ) endendclass MissingImplementation < SainityCheck def initialize super @name = "MissingImplementation" @title = "Missing Function Implementation" @type = "Garbage" @desc = "Forgetting to remove a function-definition after having removed it " @desc += "from the .cpp file only leads to cluttered header files. The only " @desc += "case where non-implemented functions should be used is when it is " @desc += "necesarry to prevent usage of for instance assignment between " @desc += "instances of a class." @declarations = Array.new @definitions = Array.new end def results @definitions.each do |definition| @declarations.delete_if do |pair| pair.first == definition end end return @declarations.map do |pair| Result.new( self, pair.last.first, pair.last.last ) end end def parse_file(file, lines) if file =~ /\.h$/ then level = 0 tree = Array.new lines.each do |line| level += line.text.count( "{" ) - line.text.count( "}" ) tree.delete_if do |struct| struct.first > level end if line.text !~ /^\s*\/\// and line.text !~ /^\s*#/ and line.text !~ /typedef/ then if line.text =~ /^\s*(class|struct)\s+/ and line.text.count( ";" ) == 0 then cur_level = level; if line.text.count( "{" ) == 0 then cur_level += 1 end entry = [ cur_level, line.text.scan( /^\s*(class|struct)\s+([^\:{;]+)/ ).first.last.split.last ] tree << entry elsif line.text =~ /;\s*$/ and line.text.count( "{" ) == 0 then # No pure virtual functions and no return(blah) calls (which otherwise can fit the requirements) if line.text !~ /=\s*0\s*;\s*$/ and line.text !~ /return/ then re = /^\s*(virtual\s+|static\s+|inline\s+|)\w+(\s+[\*\&]?|[\*\&]\s+)(\w+)\(/.match( line.text ) if re and level > 0 and tree.last then @declarations << [ tree.last.last + "::" + re[ re.length - 1 ], [ file, line ] ] end end end end end else lines.each do |line| if line.text =~ /\b\w+::\w+\s*\([^;]+$/ @definitions << line.text.scan( /\b(\w+::\w+)\s*\([^;]+$/ ).first.first end end end endend# List of enabled filtersfilterList = Array.newfilterList.push CompareAgainstEmptyString.newfilterList.push AssignmentToEmptyString.newfilterList.push NoIfNDef.newfilterList.push ThisDeference.newfilterList.push Assert.newfilterList.push PassByValue.newfilterList.push CStr.newfilterList.push IfNotDefined.newfilterList.push GPLLicense.newfilterList.push Copyright.newfilterList.push PartOfAmule.newfilterList.push MissingBody.newfilterList.push Translation.newfilterList.push IfZero.newfilterList.push InlinedIfTrue.newfilterList.push LoopOnConstant.newfilterList.push MissingImplementation.new# Sort enabled filters by type and name. The reason why this is done here is# because it's much easier than manually resorting every time I add a filter# or change the name or type of an existing filter.filterList.sort! do |x,y| cmp = x.type <=> y.type if cmp == 0 then x.title <=> y.title else cmp endenddef parse_files( path, filters ) filters = filters.dup require "find" Find.find( path ) do |filename| if filename =~ /\.(cpp|h)$/ and not filename =~ /CryptoPP\./ File.open(filename, "r") do |aFile| # Read lines and add line-numbers lines = Array.new aFile.each_line do |line| lines.push( Line.new( aFile.lineno, line ) ) end lines.freeze # Check the file against each filter filters.each do |filter| # Process the file with this filter filter.parse_file( filename, lines ) end end end end results = Array.new filters.each do |filter| results += filter.results end resultsend# Helper-functiondef get_val( key, list ) if not list.last or list.last.first != key then list << [ key, Array.new ] end list.last.lastenddef create_result_tree( path, filters ) # Gather the results results = parse_files( path, filters ) # Sort the results by the following sequence of variables: Path -> File -> Filter -> Line results.sort! do |a, b| if (a.file_path <=> b.file_path) == 0 then if (a.file_name <=> b.file_name) == 0 then if (a.type.title <=> b.type.title) == 0 then a.line.number <=> b.line.number else a.type.title <=> b.type.title end else a.file_name <=> b.file_name end else a.file_path <=> b.file_path end end # Create a tree of results: [ Path, [ File, [ Filter, [ Line ] ] ] ] result_tree = Array.new results.each do |result| get_val( result.type, get_val( result.file_name, get_val( result.file_path, result_tree ) ) ) << result end result_treeenddef create_filter_tree( filters ) # Change the filterList to a tree: [ Type, [ Filter ] ] filter_tree = Array.new filters.each do |filter| get_val( filter.type, filter_tree ) << filter end filter_treeend# Converts a number to a string and pads with zeros so that length becomes at least 5def PadNum( number ) num = number.to_s if ( num.size < 5 ) ( "0" * ( 5 - num.size ) ) + num else num endend# Helper-function that escapes some chars to HTML codesdef HTMLEsc( str ) str.gsub!( /\&/, "&" ) str.gsub!( /\"/, """ ) str.gsub!( /</, "<" ) str.gsub!( />/, ">" ) str.gsub!( /\n/, "<br>" ) str.gsub( /\t/, " " )end# Fugly output code goes here# ... Abandon hope, yee who read past here# TODO Enable use of templates.# TODO Abandon hope.def OutputHTML( filters, results ) text = "<html> <head> <STYLE TYPE=\"text/css\"> <!-- .dir { background-color: \#A0A0A0; padding-left: 10pt; padding-right: 10pt; } .file { background-color: \#838383; padding-left: 10pt; padding-right: 10pt; } .filter { background-color: #757575; padding-left: 10pt; padding-right: 10pt; padding-top: 5pt; } --> </STYLE> </head> <body bgcolor=\"\#BDBDBD\"> <h1>Filters</h1> <dl>" # List the filters filters.each do |filterType| text += " <dt><b>#{filterType.first}</b></dt> <dd> <dl>" filterType.last.each do |filter| text += " <dt id=\"#{filter.name}\"><i>#{filter.title}</i></dt> <dd> #{HTMLEsc(filter.desc)}<p> </dd>" end text +=" </dl> </dd>" end text += " </dl> <p> <h1>Directories</h1> <ul>" # List the directories results.each do |dir| text +=" <li> <a href=\"\##{dir.first}\">#{dir.first}</a> </li>" end text += " </ul> <p> <h1>Results</h1>" results.each do |dir| text += " <div class=\"dir\"> <h2 id=\"#{dir.first}\">#{dir.first}</h2>" dir.last.each do |file| text += " <div class=\"file\"> <h3>#{file.first}</h3> <ul>" file.last.each do |filter| text += " <li> <div class=\"filter\"> <b><a href=\"\##{filter.first.name}\">#{filter.first.title}</a></b> <ul>" filter.last.each do |result| if result.line then text += " <li><b>#{PadNum(result.line.number)}:</b> #{HTMLEsc(result.line.text.strip)}</li>" end end text +=" </ul> </div> </li>" end text += " </ul> </div>" end text += " </div> <p>" end text += " </body></html>" return text;end# Columnizing, using the http://www.rubygarden.org/ruby?UsingTestUnit example because I'm lazy# TODO Rewrite it to better support newlines and stuffdef Columnize( text, width, indent ) return indent + text.scan(/(.{1,#{width}})(?: |$)/).join("\n#{indent}")end# Fugly output code also goes here, this is a bit more sparse than the HTML stuffdef OutputTEXT( filters, results ) # List the filters text = "Filters\n" filters.each do |filterType| text += "\t* #{filterType.first}\n" filterType.last.each do |filter| text += "\t\t- #{filter.title}\n" text += Columnize( filter.desc, 80, "\t\t\t" ) + "\n\n" end end # List the directories text += "\n\nDirectories\n" results.each do |dir| text += "\t#{dir.first}\n" end text += "\n\nResults\n" # To avoid bad readability, I only use fullpaths here instead of sections per dir results.each do |dir| dir.last.each do |file| text += "\t#{dir.first}#{file.first}\n" file.last.each do |filter| text += "\t\t* #{filter.first.title}\n" filter.last.each do |result| if result.line then text += "\t\t\t#{PadNum(result.line.number)}: #{result.line.text.strip}\n" end end end text += "\n" end end return text;end#TODO Improved parameter-handling, add =<file> for the outputing to a fileARGV.each do |param| case param when "--text" then puts OutputTEXT( create_filter_tree( filterList ), create_result_tree( ".", filterList ) ) when "--html" then puts OutputHTML( create_filter_tree( filterList ), create_result_tree( ".", filterList ) ) endend
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -