Beispiel #1
0
 def grade_student_file(self, filename):
     extension = filename.split('.')[-1]
     if extension not in ['h', 'cpp']:
         sys.stderr.write('Failed to parse {}: incorrect file type.\n'.format(filename))
         return
     data = safely_open(filename)
     if data:
         self.reset_for_new_file(filename)
         raw_data = deepcopy(data)
         RemoveMultiLineComments(filename, data, '')
         clean_lines = CleansedLines(data)
         clean_code = clean_lines.lines
         for self.current_line_num, code in enumerate(clean_code):
             for function in self.single_line_checks: function(self, code)
             for function in self.multi_line_checks: function(self, clean_lines)
         # COMMENT CHECKS #TODO
         for self.current_line_num, text in enumerate(raw_data):
             if self.config.get('COMMENT_CHECKS', 'line_width').lower() == 'yes':
                 getattr(comment_checks, 'check_line_width')(self, text)
             if check_if_function(text):
                 if self.config.get('COMMENT_CHECKS', 'missing_rme').lower() == 'yes':
                     getattr(comment_checks, 'check_missing_rme')(self, raw_data)
             if self.config.get('COMMENT_CHECKS', 'min_comments').lower() == 'yes':
                 getattr(comment_checks, 'check_min_comments')(self, raw_data, clean_code)
         for function in self.misc_checks: function(self)
         self.error_tracker[filename].sort()
         self.file_has_a_main[filename] = not self.outside_main
    def check_brace_consistency(self, clean_lines):
        code = clean_lines.lines[self.current_line_num]
        stripped_code = code.strip()
        function = check_if_function(code)
        if_statement = re.search(r'^if\s*\(\s*', stripped_code)
        else_if_statement = re.search(r'^else\s*\(', code)
        else_statement = re.search(r'^else\s+', code)
        switch_statement = re.search(r'^switch\s*\(', stripped_code)
        #TODO: Clean this line up
        if function or if_statement or else_statement or switch_statement:
            if function and clean_lines.lines[self.current_line_num + 1].find('{') != -1 or\
                else_if_statement and clean_lines.lines[self.current_line_num + 1].find('{') != -1 or\
                else_statement and clean_lines.lines[self.current_line_num + 1].find('{') != -1 or\
                switch_statement and clean_lines.lines[self.current_line_num + 1].find('{') != -1 or\
                    if_statement and clean_lines.lines[self.current_line_num + 1].find('{') != -1:

                self.set_egyptian_style(False)
            elif function and code.find('{') != -1 or \
                    else_if_statement and code.find('{') != -1 or\
                    else_statement and code.find('{') != -1 or\
                    switch_statement and code.find('{') != -1 or\
                    if_statement and code.find('{') != -1:

                self.set_egyptian_style(True)
            elif not self.is_outside_main():
                self.add_error("BRACES_ERROR")

            #if both of these are true, they are not consistent, therefore error.
            if self.notEgyptian:
                if self.egyptian:
                    self.add_error("BRACES_ERROR")
    def check_non_const_global(self, code):
        if re.search(r'int main', code):
            self.set_inside_main()

        if self.is_outside_main():
            function = check_if_function(code)
            variable = re.search(r'^\s*(int|string|char|bool)\s+', code)
            if not function and variable:
                self.add_error("GLOBAL_VARIABLE")
def check_function_def_above_main(self, code):
    prototype = check_if_function_prototype(code)
    function = check_if_function(code)
    inside = Literal("int main")
    if len(inside.searchString(code)):
        return
    elif function and not prototype and self.outside_main:
        function_regex = re.compile("^\s*(\w+)\s+(\w+)")
        match = function_regex.search(code)
        function_name = match.group(2) if match else "NOT_FOUND"
        self.add_error(label="DEFINITION_ABOVE_MAIN", data={'function': function_name})
def check_int_for_bool(self, code):
    if check_if_function(code):
        function_regex = re.compile("^\s*(\w+)\s+(\w+)")
        match = function_regex.search(code)
        if match:
            self.current_function = (match.group(1), match.group(2))
    current_function = getattr(self, "current_function", ("", ""))

    return_regex = re.compile("\s*return\s+(\w+)")
    match = return_regex.search(code)
    if match and match.group(1).isdigit() and current_function[0] == "bool":
        self.add_error(label="INT_FOR_BOOL")
Beispiel #6
0
def check_int_for_bool(self, code):
    if check_if_function(code):
        function_regex = re.compile(r"^\s*(\w+)\s+(\w+)")
        match = function_regex.search(code)
        if match:
            self.current_function = (match.group(1), match.group(2))
    current_function = getattr(self, "current_function", ("", ""))

    return_regex = re.compile(r"\s*return\s+(\w+)")
    match = return_regex.search(code)
    if match and match.group(1).isdigit() and current_function[0] == "bool":
        self.add_error(label="INT_FOR_BOOL")
def check_non_const_global(self, code):
    inside = Literal("int main")
    if len(inside.searchString(code)):
        self.outside_main = False

    if self.outside_main:
        function = check_if_function(code)
        variables = variables = re.compile("^(?:\w|_)+\s+(?:\w|_|\[|\])+\s*=\s*.+;")
        keywords = re.compile("^\s*(?:using|class|struct)")
        constants = re.compile("^\s*(?:static\s+)?const")
        if not function and variables.search(code) and \
                not keywords.search(code) and \
                not constants.search(code):
            self.add_error(label="NON_CONST_GLOBAL")
Beispiel #8
0
def check_non_const_global(self, code):
    inside = Literal("int main")
    if len(inside.searchString(code)):
        self.outside_main = False

    elif self.outside_main:
        function = check_if_function(code)
        variables = variables = re.compile(
            r"^(?:\w|_)+\s+(?:\w|_|\[|\])+\s*=\s*.+;")
        keywords = re.compile(r"^\s*(?:using|class|struct)")
        constants = re.compile(r"^\s*(?:static\s+)?const")
        if not function and variables.search(code) and \
                not keywords.search(code) and \
                not constants.search(code):
            self.add_error(label="NON_CONST_GLOBAL")
    def check_function_block_indentation(self, clean_lines, operator_space_tracker):
        tab_size = 4
        code = clean_lines.lines[self.current_line_num]
        stripped_code = code.strip()
        function = check_if_function(code)
        if_statement = re.search(r'^if\s*\(\s*', stripped_code)
        else_if_statement = re.search(r'^else\s*\(', code)
        else_statement = re.search(r'^else\s+', code)
        switch_statement = re.search(r'^switch\s*\(', stripped_code)

        indentation = re.search(r'^( *)\S', code)
        if indentation:
            indentation = indentation.group()
            indentation_size = len(indentation) - len(indentation.strip())
        else:
            return

        if function or self.is_outside_main():
            if indentation_size != 0:
                self.add_error("INDENTATION_ERROR")
            if self.is_outside_main():
                return

        #TODO: Need to check indentation ON the same line as the function still
        if function:
            #if not egyptian style
            if code.find('{') == -1:
                second_line = clean_lines.lines[self.current_line_num + 1]

                if code.find('{'):
                    temp_line_num = self.current_line_num + 1
                    data_structure_tracker = DataStructureTracker()
                    data_structure_tracker.brace_stack.append('{')
                    self.process_current_blocks_indentation(indentation, tab_size, code,
                                             clean_lines, data_structure_tracker,
                                             temp_line_num)
                else:
                    #TODO Figure out what it means to not have braces in the right place
                    pass
            else:
                temp_line_num = self.current_line_num
                data_structure_tracker = DataStructureTracker()
                data_structure_tracker.brace_stack.append('{')
                self.process_current_blocks_indentation(indentation, tab_size, code,
                                                   clean_lines, data_structure_tracker,
                                                   temp_line_num)
        else:
            return
Beispiel #10
0
def check_brace_consistency(self, clean_lines):
    code = clean_lines.lines[self.current_line_num]
    stripped_code = code.strip()
    function = check_if_function(code)
    if_statement = re.search(r'^if\s*\(\s*', stripped_code)
    else_if_statement = re.search(r'^else\s*\(', code)
    else_statement = re.search(r'^else\s+', code)
    switch_statement = re.search(r'^switch\s*\(', stripped_code)
    indentation = re.search(r'^( *)\S', code)

    if indentation:
        indentation = indentation.group()
        indentation_size = len(indentation) - len(indentation.strip())
    else:
        indentation_size = 0

    current = self.current_line_num
    if function or if_statement or else_statement or switch_statement:
        try:
            if function or \
                else_if_statement or\
                else_statement  or\
                switch_statement or\
                if_statement:
                    if deep_egyptian_check(clean_lines.lines, indentation_size, current):
                        self.egyptian = True
                    else:
                         self.not_egyptian = True

            elif not self.outside_main:
                if not self.braces_error:
                    self.add_error(label="BRACE_CONSISTENCY")
                    self.braces_error = True

            if self.not_egyptian and self.egyptian and not self.braces_error:
                 self.add_error(label="BRACE_CONSISTENCY")
                 self.braces_error = True

            #if both of these are true, they are not consistent, therefore error.
            if self.not_egyptian:
                if self.egyptian and not self.braces_error:
                    self.add_error(label="BRACE_CONSISTENCY")
                    self.braces_error = True

        except IndexError:
            # cannot access next line of end of file, rubric properties don't matter
            return
def check_operator_spacing(self, code):
    # TODO: Temporary fix to ignore & and * operators in function params
    if check_if_function(code) or check_if_function_prototype(code) or\
            '#include' in code: return
    # Check normal operators
    # account for *=, %=, /=, +=, -=
    indexes = []
    indexes = findOccurences(code, '+') + \
              findOccurences(code, '-') + \
              findOccurences(code, '%') + \
              findOccurences(code, '*') + \
              findOccurences(code, '/') + \
              findOccurences(code, '!') + \
              findOccurences(code, '>') + \
              findOccurences(code, '<') + \
              findOccurences(code, '=') + \
              findOccurences(code, '&') + \
              findOccurences(code, '|')
    indexes.sort()  # Force compound operator indexes to be correctly ordered

    skip_next = False
    for operator_index in indexes:
        if skip_next:
            # skip second operator in compound/increment/decrement operator
            skip_next = False
            continue
        
        if skip_operator(code, operator_index):
            skip_next = True
        elif is_compound_operator(code, operator_index):
            # Always use front char in compound operators, therefore need to skip second char
            skip_next = True
            if not operator_helper(True, code, operator_index):
                self.add_error(label='OPERATOR_SPACING', column=operator_index,
                                data={'operator': code[operator_index:operator_index + 2]})
        else:
            # TODO: Add code checking for unary + and - operators
            if code[operator_index] == '!':
                index = operator_index - 1
                # Only check for spacing in front of ! (NOT) operator
                if code[index]:
                    if code[index] not in [' ', '\r', '\n', '(']:
                            self.add_error(label='OPERATOR_SPACING', column=operator_index, data={'operator': code[operator_index]})
            elif not operator_helper(False, code, operator_index):
                self.add_error(label='OPERATOR_SPACING', column=operator_index, data={'operator': code[operator_index]})
def check_brace_consistency(self, clean_lines):
    code = clean_lines.lines[self.current_line_num]
    stripped_code = code.strip()
    function = check_if_function(code)
    if_statement = re.search(r'^if\s*\(\s*', stripped_code)
    else_if_statement = re.search(r'^else\s*\(', code)
    else_statement = re.search(r'^else\s+', code)
    switch_statement = re.search(r'^switch\s*\(', stripped_code)
    #TODO: Clean this line up

    if function or if_statement or else_statement or switch_statement:

        try:
            if function and code.find('{') != -1 or \
                    else_if_statement and code.find('{') != -1 or\
                    else_statement and code.find('{') != -1 or\
                    switch_statement and code.find('{') != -1 or\
                    if_statement and code.find('{') != -1:

                self.egyptian = True

            elif function and clean_lines.lines[self.current_line_num + 1].find('{') != -1 or\
                else_if_statement and clean_lines.lines[self.current_line_num + 1].find('{') != -1 or\
                else_statement and clean_lines.lines[self.current_line_num + 1].find('{') != -1 or\
                switch_statement and clean_lines.lines[self.current_line_num + 1].find('{') != -1 or\
                    if_statement and clean_lines.lines[self.current_line_num + 1].find('{') != -1:

                self.not_egyptian = True

            elif not self.outside_main:
                if not self.braces_error:
                    self.add_error(label="BRACE_CONSISTENCY")
                    self.braces_error = True

            #if both of these are true, they are not consistent, therefore error.
            if self.not_egyptian:
                if self.egyptian and not self.braces_error:
                    self.add_error(label="BRACE_CONSISTENCY")
                    self.braces_error = True

        except IndexError:
            # cannot access next line of end of file, rubric properties don't matter
            return
Beispiel #13
0
 def grade_student_file(self, filename):
     extension = filename.split('.')[-1]
     if extension not in ['h', 'cpp']:
         sys.stderr.write('Failed to parse {}: incorrect file type.\n'.format(filename))
         return
     data = safely_open(filename)
     if data:
         self.reset_for_new_file(filename)
         raw_data = deepcopy(data)
         RemoveMultiLineComments(filename, data, '')
         clean_lines = CleansedLines(data)
         clean_code = clean_lines.lines
         for self.current_line_num, code in enumerate(clean_code):
             code = erase_string(code)
             # TODO: Improve the check for 'Is this a #include line?'
             if '#include' in code:
                 continue
             if code.find('\t') != -1:
                 self.add_error(label='USING_TABS')
                 break
             if self.current_line_num == 114:
                 print "stop"
             for function in self.single_line_checks: function(self, code)
             for function in self.multi_line_checks: function(self, clean_lines)
         # COMMENT CHECKS #TODO
         for self.current_line_num, text in enumerate(raw_data):
             if self.config.get('COMMENT_CHECKS', 'line_width').lower() == 'yes':
                 getattr(comment_checks, 'check_line_width')(self, text)
             if check_if_function(text):
                 if self.config.get('COMMENT_CHECKS', 'missing_rme').lower() == 'yes':
                     getattr(comment_checks, 'check_missing_rme')(self, raw_data)
             if self.config.get('COMMENT_CHECKS', 'min_comments').lower() == 'yes':
                 getattr(comment_checks, 'check_min_comments')(self, raw_data, clean_code)
         for function in self.misc_checks: function(self)
         self.error_tracker[filename].sort()
         self.file_has_a_main[filename] = not self.outside_main
         if not self.file_has_a_main[filename]:
             self.remove_def_above_main_errors(filename)
Beispiel #14
0
    def grade_student_file(self, filename, original_filename):
        extension = filename.split('.')[-1]
        if extension not in ['h', 'cpp']:
            sys.stderr.write(
                'Failed to parse {}: incorrect file type.\n'.format(filename))
            return
        data = safely_open(filename)
        if data:
            self.reset_for_new_file(filename)
            raw_data = deepcopy(data)
            RemoveMultiLineComments(filename, data, '')
            clean_lines = CleansedLines(data)
            clean_code = clean_lines.elided

            for function in self.filename_checks:
                function(self, original_filename)
            for self.current_line_num, code in enumerate(clean_code):
                code = erase_string(code)
                if self.config.get('SINGLE_LINE_CHECKS', 'tab_type').lower(
                ) == 'soft' and code.find('\t') != -1:
                    self.add_error(label='USING_TABS')
                    break

                for function in self.single_line_checks:
                    function(self, code)
                for function in self.multi_line_checks:
                    function(self, clean_lines)

            # COMMENT CHECKS #TODO
            for self.current_line_num, text in enumerate(raw_data):
                if self.config.get('COMMENT_CHECKS',
                                   'line_width').lower() == 'yes':
                    getattr(comment_checks, 'check_line_width')(self, text)
                if check_if_function(text):
                    if self.config.get('COMMENT_CHECKS',
                                       'missing_rme').lower() == 'yes':
                        getattr(comment_checks, 'check_missing_rme')(self,
                                                                     raw_data)
                if check_if_function_prototype(text):
                    if self.config.get(
                            'COMMENT_CHECKS',
                            'missing_prototype_comments').lower() == 'yes':
                        getattr(comment_checks,
                                'check_missing_prototype_comments')(
                                    self,
                                    clean_lines.lines_without_raw_strings)
                if self.config.get('COMMENT_CHECKS',
                                   'min_comments').lower() == 'yes':
                    getattr(comment_checks,
                            'check_min_comments')(self, raw_data, clean_code)
                if self.config.get('COMMENT_CHECKS',
                                   'missing_type_comments').lower() == 'yes':
                    getattr(comment_checks, 'check_missing_type_comments')(
                        self, clean_lines.lines_without_raw_strings)
                if self.config.get('COMMENT_CHECKS',
                                   'comment_spacing').lower() == 'yes':
                    getattr(comment_checks, 'check_comment_spacing')(
                        self, clean_lines.lines_without_raw_strings)
            for function in self.misc_checks:
                function(self)

            # Run checks directly from cpplint
            # Filters are configurable from the CPPLINT.cfg file
            self.cpplint_tests(filename)

            # Organize the error list for this file
            self.error_tracker[filename].sort()
            self.file_has_a_main[filename] = not self.outside_main
            if not self.file_has_a_main[filename]:
                self.remove_error_by_type(filename, 'DEFINITION_ABOVE_MAIN')

            # This is a bit of a hack, to make the setting work.
            # TODO: make a multi-line check for unnecessary breaks.
            if not self.detect_unnecessary_break:
                self.remove_error_by_type(filename, 'UNNECESSARY_BREAK')
Beispiel #15
0
def check_block_indentation(self, clean_lines):
    #TODO: Load from config file? 
    tab_size = 4

    code = clean_lines.lines[self.current_line_num]
    print code

    if check_if_struct_or_class(code):
        self.global_in_object = True

    if self.global_in_object and code.find('{') != -1:
        self.add_global_brace('{')
    elif self.global_in_object and code.find('}') != -1:
        self.pop_global_brace()

    function = check_if_function(code)
    struct_or_class = check_if_struct_or_class(code)
    indentation = re.search(r'^( *)\S', code)
    if indentation:
        indentation = indentation.group()
        indentation_size = len(indentation) - len(indentation.strip())
    else:
        return

    if function and indentation_size != 0 and not self.global_in_object and code.find('else if') == -1:
        data = {'expected': 0, 'found': indentation_size}
        self.add_error(label="BLOCK_INDENTATION", data=data)

    if function:
        self.current_line_num = find_function_end(clean_lines.lines, self.current_line_num)

    if (function and not self.outside_main) or struct_or_class:
        #if not egyptian style
        if code.find('{') == -1:
            if code.find('{'):
                temp_line_num = self.current_line_num + 1
                data_structure_tracker = DataStructureTracker()
                data_structure_tracker.brace_stack.append('{')

                if check_if_struct_or_class(code):
                    data_structure_tracker.in_class_or_struct = True
                if self.global_in_object:
                    self.add_global_brace('{')
                    data_structure_tracker.add_object_brace('{')

                results = indent_helper(indentation, tab_size, clean_lines, 
                                        data_structure_tracker, temp_line_num)

                for error in results:
                    self.add_error(**error)
            else:
                #TODO Figure out what it means to not have braces in the right place
                pass
        else:
            temp_line_num = self.current_line_num
            data_structure_tracker = DataStructureTracker()

            if check_if_struct_or_class(code):
                data_structure_tracker.add_object_brace("{")
                data_structure_tracker.in_class_or_struct = True

            data_structure_tracker.brace_stack.append('{')
            results = indent_helper(indentation, tab_size, clean_lines, 
                                    data_structure_tracker, temp_line_num)
            for error in results:
                self.add_error(**error)
    else:
        return
Beispiel #16
0
def check_identifier_case(self, code):
    if code.isspace():
        return

    # check if the first char is lower-case alpha or '_'
    lowercase = re.compile(r"(?:^|\s+)(?:class|struct|enum)\s+(?:[a-z]|_)\w*")
    bad_naming = lowercase.search(code)

    if bad_naming:
        result = bad_naming.group(0).split()
        expected = str(result[1])

        if len(expected) == 1:
            expected = 'A Descriptive Name'
        if (expected and expected[0]
                == '_'):  # Remove leading _ from expected input
            expected = expected[1:]
        self.add_error(label="IDENTIFIER_CASE",
                       data={
                           "style":
                           "camel case (starting with a capital letter)",
                           "type":
                           result[0],
                           "expected":
                           expected[0].capitalize() +
                           (expected[1:] if len(expected) > 1 else ''),
                           "found":
                           str(result[1])
                       })
        return

    # Make sure the first letter of non-const variable names are lowercase.
    uppercase = re.compile(
        r'(?:^|\s+)(?<!const\s)\s*(?:void|bool|char|short|long|int|float|double|string|std::string|auto|ifstream|ofstream)[\*\&\s]+(?:[\w_]+\:\:)*((?:[A-Z]|_)\w+)\s*[,\[\(\)\{;=]'
    )
    bad_naming = uppercase.search(code)
    uppercase_unsigned = re.compile(
        r'(?:^|\s+)const\s+(?:signed|unsigned)\s+(?:bool|char|short|long|int|float|double)[\*\&\s]+(?:[\w_]+\:\:)*((?:[A-Z]|_)\w+)\s*[,\(\)\{;=]'
    )
    bad_underscore = re.search(
        r'(?:^|\s+)(?<!const\s)\s*(?:void|bool|char|short|long|int|float|double|string|std::string|auto|ifstream|ofstream)[\*\&\s]+(?:[\w_]+\:\:)*((?:[\w_])\w*_[\w_]*)\s*[,\[\(\)\{;=]',
        code)

    if (bad_naming and not uppercase_unsigned.search(code)) or bad_underscore:

        result = bad_naming.group(1) if bad_naming else bad_underscore.group(1)

        # Create an expected constant name where underscores are converted to camel case
        try:
            expected = ''
            var_length = len(result)
            cap_next = False
            for i, ch in enumerate(result):
                if ch == '_':
                    cap_next = True
                elif cap_next:
                    expected += ch.upper()
                    cap_next = False
                else:
                    expected += ch

            if (expected and expected[0]
                    == '_'):  # Remove leading _ from expected input
                expected = expected[1:]

            self.add_error(label="IDENTIFIER_CASE",
                           data={
                               "type":
                               'non-constant variables or function',
                               "style":
                               "camel case (starting with a lowercase letter)",
                               "expected": ((expected[:1].lower() +
                                             expected[1:]) if expected else '')
                               if len(expected) > 1 else "a descriptive name",
                               "found":
                               str(result)
                           })
        except IndexError:
            # probably means that this is an std:: parameter, they don't need to be capitalized.
            print("Something weird happened in check_identifier_case with '",
                  code, "'.")
            return
        return

    # Make sure const variables are all caps
    if not check_if_function_prototype(code) and not check_if_function(code):
        const_var = re.compile(
            r"(?:^|\s+)const\s+(?:void|bool|char|short|long|int|float|double|string|std::string|auto)\s*[\*\&\s]*\s*(?:\w|_)\w+"
        )
        const_var = const_var.search(code)
        unsigned_const_var = re.search(
            r'(?:^|\s+)const\s+(?:signed|unsigned)\s+(?:char|short|long|long\s+long|int)\s*[\*\&\s]*\s*(?:\w|_)\w+',
            code)
        if const_var or unsigned_const_var:
            if const_var:
                const_var = str(const_var.group(0).split()[-1])
            else:
                const_var = str(unsigned_const_var.group(0).split()[-1])

            # Create an expected constant name where camel case is converted to all caps with underscores
            expected = ''
            var_length = len(const_var)
            for i, ch in enumerate(reversed(const_var)):
                if ch.isupper(
                ) and ch != '_' and i < var_length - 1 and i > 0 and const_var[
                        var_length - i - 2] != '_':
                    expected = '_' + ch + expected
                else:
                    expected = ch.upper() + expected

            if (expected and expected[0]
                    == '_'):  # Remove leading _ from expected input
                expected = expected[1:]
            if not const_var.isupper():
                self.add_error(
                    label="IDENTIFIER_CASE",
                    data={
                        "type": 'constant variable',
                        "style":
                        "all caps (separating words with an underscore)",
                        "expected": expected
                        if len(expected) > 1 else "a descriptive name",
                        "found": const_var
                    })