diff --git a/norminette/norm_error.py b/norminette/norm_error.py index 6a58a4e3..fd476167 100644 --- a/norminette/norm_error.py +++ b/norminette/norm_error.py @@ -109,7 +109,7 @@ "BRACE_NEWLINE": "Expected newline before brace", "EXP_NEWLINE": "Expected newline after control structure", "ARG_TYPE_UKN": "Unrecognized variable type", - "COMMENT_ON_INSTR": "Comment must be on its own line", + "COMMENT_ON_INSTR": "Comment must be on its own line or at end of a line", "COMMA_START_LINE": "Comma at line start", "MIXED_SPACE_TAB": "Mixed spaces and tabs", "ATTR_EOL": "Function attribute must be at the end of line", diff --git a/norminette/rules/check_comment.py b/norminette/rules/check_comment.py index 17faf940..8ea721d3 100644 --- a/norminette/rules/check_comment.py +++ b/norminette/rules/check_comment.py @@ -1,34 +1,56 @@ from norminette.rules import Rule -allowed_on_comment = ["COMMENT", "MULT_COMMENT", "SPACE", "TAB"] - class CheckComment(Rule): - def __init__(self): - super().__init__() - self.depends_on = [] - def run(self, context): """ - Comments are only allowed in GlobalScope. + Comments are forbidden inside functions and in the middle of instructions. """ i = context.skip_ws(0) - has_comment = False - while ( - context.peek_token(i) is not None - and context.check_token(i, "NEWLINE") is False - ): - if context.check_token(i, allowed_on_comment) is False: - if has_comment is True: - context.new_error("COMMENT_ON_INSTR", context.peek_token(i)) - return True, i - elif context.check_token(i, ["COMMENT", "MULT_COMMENT"]) is True: - if ( - context.scope.name != "GlobalScope" - or context.history[-1] == "IsFuncDeclaration" - ): - context.new_error("WRONG_SCOPE_COMMENT", context.peek_token(i)) - has_comment = True + + tokens = [] + while context.peek_token(i) and not context.check_token(i, "NEWLINE"): + token = context.peek_token(i) + tokens.append(token) i += 1 - i = context.skip_ws(0) - return False, 0 + + for index, token in enumerate(tokens): + if token.type in ("COMMENT", "MULT_COMMENT"): + if self.is_inside_a_function(context): + context.new_error("WRONG_SCOPE_COMMENT", token) + if index == 0 or self.is_last_token(token, tokens[index+1:]): + continue + context.new_error("COMMENT_ON_INSTR", token) + + def is_inside_a_function(self, context): + if context.history[-2:] == ["IsFuncDeclaration", "IsBlockStart"]: + return True + if context.scope.__class__.__name__.lower() == "function": + return True + # Sometimes the context scope is a `ControlStructure` scope instead of + # `Function` scope, so, to outsmart this bug, we need check manually + # the `context.history`. + last = None + for index, record in enumerate(reversed(context.history)): + if record == "IsFuncDeclaration" and last == "IsBlockStart": + # Since the limited history API, we can't say if we're in a + # nested function to reach the first enclosing function, so, + # we'll consider that the user just declared a normal function + # in global scope. + stack = 1 + index -= 1 # Jumps to next record after `IsBlockStart` + while index > 0 and stack > 0: + record = context.history[-index] + index -= 1 + if record not in ("IsBlockStart", "IsBlockEnd"): + continue + stack = stack + (1, -1)[record == "IsBlockEnd"] + return bool(stack) + last = record + return False + + def is_last_token(self, token, foward): + expected = ("SPACE", "TAB") + if token.type == "MULT_COMMENT": + expected += ("COMMENT", "MULT_COMMENT") + return all(it.type in ("SPACE", "TAB", "COMMENT", "MULT_COMMENT") for it in foward) diff --git a/norminette/rules/check_func_arguments_name.py b/norminette/rules/check_func_arguments_name.py index 1a54f84d..61d7decd 100644 --- a/norminette/rules/check_func_arguments_name.py +++ b/norminette/rules/check_func_arguments_name.py @@ -29,7 +29,7 @@ def check_arg_format(self, context, pos): p = 0 stop = ["COMMA", "RPARENTHESIS"] if context.check_token(i, ["COMMENT", "MULT_COMMENT"]): - context.new_error("WRONG_SCOPE_COMMENT", context.peek_token(i)) + # context.new_error("WRONG_SCOPE_COMMENT", context.peek_token(i)) i += 1 # if context.check_token(i, "NEWLINE"): # context.new_error("NEWLINE_IN_DECL", context.peek_token(i)) diff --git a/tests/rules/samples/test_comments.c b/tests/rules/samples/test_comments.c new file mode 100644 index 00000000..78b59007 --- /dev/null +++ b/tests/rules/samples/test_comments.c @@ -0,0 +1,21 @@ +struct { + // points is to something + int points; // is an int :D +}; + +typedef /* oopss */ bool bool; + +enum test { + // blaboe + hello, // it works + /* error*/ error +}; + +void hello(/* nothing */ void) // error because comment is in middle of the line +{ + // error because scope is from a function + { + // are you trying to cheat? + // error because scope is from a function + } +} diff --git a/tests/rules/samples/test_comments.out b/tests/rules/samples/test_comments.out new file mode 100644 index 00000000..95773bf6 --- /dev/null +++ b/tests/rules/samples/test_comments.out @@ -0,0 +1,75 @@ +test_comments.c - IsUserDefinedType In "GlobalScope" from "None" line 1": + +test_comments.c - IsComment In "UserDefinedType" from "GlobalScope" line 2": + +test_comments.c - IsVarDeclaration In "UserDefinedType" from "GlobalScope" line 3": + +test_comments.c - IsComment In "UserDefinedType" from "GlobalScope" line 3": + +test_comments.c - IsBlockEnd In "UserDefinedType" from "GlobalScope" line 4": + +test_comments.c - IsEmptyLine In "GlobalScope" from "None" line 5": + +test_comments.c - IsUserDefinedType In "GlobalScope" from "None" line 6": + +test_comments.c - IsEmptyLine In "GlobalScope" from "None" line 7": + +test_comments.c - IsUserDefinedType In "GlobalScope" from "None" line 8": + +test_comments.c - IsComment In "UserDefinedEnum" from "GlobalScope" line 9": + +test_comments.c - IsEnumVarDecl In "UserDefinedEnum" from "GlobalScope" line 10": + +test_comments.c - IsComment In "UserDefinedEnum" from "GlobalScope" line 10": + +test_comments.c - IsComment In "UserDefinedEnum" from "GlobalScope" line 11": + +test_comments.c - IsEnumVarDecl In "UserDefinedEnum" from "GlobalScope" line 11": + +test_comments.c - IsBlockEnd In "UserDefinedEnum" from "GlobalScope" line 12": + +test_comments.c - IsEmptyLine In "GlobalScope" from "None" line 13": + +test_comments.c - IsFuncDeclaration In "GlobalScope" from "None" line 14": + +test_comments.c - IsBlockStart In "Function" from "GlobalScope" line 15": + +test_comments.c - IsComment In "Function" from "GlobalScope" line 16": + +test_comments.c - IsBlockStart In "Function" from "GlobalScope" line 17": + +test_comments.c - IsComment In "ControlStructure" from "Function" line 18": + +test_comments.c - IsComment In "ControlStructure" from "Function" line 19": + +test_comments.c - IsBlockEnd In "ControlStructure" from "Function" line 20": + +test_comments.c - IsBlockEnd In "Function" from "GlobalScope" line 21": + +test_comments.c: Error! +Error: INVALID_HEADER (line: 1, col: 1): Missing or invalid 42 header +Error: BRACE_NEWLINE (line: 1, col: 8): Expected newline before brace +Error: COMMENT_ON_INSTR (line: 6, col: 9): Comment must be on its own line or at end of a line +Error: SPACE_REPLACE_TAB (line: 6, col: 25): Found space when expecting tab +Error: USER_DEFINED_TYPEDEF (line: 6, col: 26): User defined typedef must start with t_ +Error: ENUM_TYPE_NAMING (line: 8, col: 6): Enum name must start with e_ +Error: BRACE_NEWLINE (line: 8, col: 11): Expected newline before brace +Error: SPACE_EMPTY_LINE (line: 9, col: 5): Space on empty line +Error: TOO_FEW_TAB (line: 10, col: 1): Missing tabs for indent level +Error: SPACE_REPLACE_TAB (line: 10, col: 5): Found space when expecting tab +Error: CONSECUTIVE_SPC (line: 10, col: 11): Two or more consecutives spaces +Error: SPACE_REPLACE_TAB (line: 11, col: 5): Found space when expecting tab +Error: TOO_FEW_TAB (line: 11, col: 16): Missing tabs for indent level +Error: SPACE_BEFORE_FUNC (line: 14, col: 5): space before function name +Error: COMMENT_ON_INSTR (line: 14, col: 12): Comment must be on its own line or at end of a line +Error: MISSING_IDENTIFIER (line: 14, col: 25): missing type qualifier or identifier in function arguments +Error: SPACE_EMPTY_LINE (line: 16, col: 4): Space on empty line +Error: WRONG_SCOPE_COMMENT (line: 16, col: 4): Comment is invalid in this scope +Error: TOO_FEW_TAB (line: 17, col: 1): Missing tabs for indent level +Error: SPACE_EMPTY_LINE (line: 17, col: 4): Space on empty line +Error: SPACE_EMPTY_LINE (line: 18, col: 7): Space on empty line +Error: WRONG_SCOPE_COMMENT (line: 18, col: 7): Comment is invalid in this scope +Error: SPACE_EMPTY_LINE (line: 19, col: 7): Space on empty line +Error: WRONG_SCOPE_COMMENT (line: 19, col: 7): Comment is invalid in this scope +Error: TOO_FEW_TAB (line: 20, col: 1): Missing tabs for indent level +Error: SPACE_EMPTY_LINE (line: 20, col: 4): Space on empty line diff --git a/tests/rules/samples/test_file_1019.out b/tests/rules/samples/test_file_1019.out index 3a10042a..9c894192 100644 --- a/tests/rules/samples/test_file_1019.out +++ b/tests/rules/samples/test_file_1019.out @@ -164,20 +164,20 @@ test_file_1019.c: Error! Error: INVALID_HEADER (line: 1, col: 1): Missing or invalid 42 header Error: RETURN_PARENTHESIS (line: 8, col: 12): Return value must be in parenthesis Error: FORBIDDEN_CHAR_NAME (line: 28, col: 9): user defined identifiers should contain only lowercase characters, digits or '_' -Error: COMMENT_ON_INSTR (line: 35, col: 25): Comment must be on its own line +Error: COMMENT_ON_INSTR (line: 35, col: 6): Comment must be on its own line or at end of a line +Error: COMMENT_ON_INSTR (line: 41, col: 10): Comment must be on its own line or at end of a line Error: WRONG_SCOPE_COMMENT (line: 41, col: 10): Comment is invalid in this scope -Error: COMMENT_ON_INSTR (line: 41, col: 29): Comment must be on its own line +Error: COMMENT_ON_INSTR (line: 42, col: 10): Comment must be on its own line or at end of a line Error: WRONG_SCOPE_COMMENT (line: 42, col: 10): Comment is invalid in this scope -Error: COMMENT_ON_INSTR (line: 42, col: 29): Comment must be on its own line Error: TOO_MANY_FUNCS (line: 46, col: 1): Too many functions in file Error: GOTO_FBIDDEN (line: 50, col: 1): Goto statements are forbidden Error: LABEL_FBIDDEN (line: 52, col: 1): Label statements are forbidden Error: TOO_MANY_FUNCS (line: 56, col: 1): Too many functions in file +Error: COMMENT_ON_INSTR (line: 62, col: 27): Comment must be on its own line or at end of a line Error: WRONG_SCOPE_COMMENT (line: 62, col: 27): Comment is invalid in this scope -Error: COMMENT_ON_INSTR (line: 62, col: 47): Comment must be on its own line Error: TOO_MANY_FUNCS (line: 66, col: 1): Too many functions in file +Error: COMMENT_ON_INSTR (line: 70, col: 14): Comment must be on its own line or at end of a line Error: WRONG_SCOPE_COMMENT (line: 70, col: 14): Comment is invalid in this scope -Error: COMMENT_ON_INSTR (line: 70, col: 32): Comment must be on its own line Error: TOO_MANY_FUNCS (line: 73, col: 1): Too many functions in file Error: SPC_BEFORE_NL (line: 73, col: 17): Space before newline Error: TOO_MANY_FUNCS (line: 78, col: 1): Too many functions in file diff --git a/tests/rules/samples/test_file_210128.out b/tests/rules/samples/test_file_210128.out index 43d8b8b8..a229cb02 100644 --- a/tests/rules/samples/test_file_210128.out +++ b/tests/rules/samples/test_file_210128.out @@ -39,7 +39,6 @@ test_file_210128.c: Error! Error: INVALID_HEADER (line: 1, col: 1): Missing or invalid 42 header -Error: WRONG_SCOPE_COMMENT (line: 1, col: 15): Comment is invalid in this scope Error: WRONG_SCOPE_COMMENT (line: 5, col: 14): Comment is invalid in this scope Error: WRONG_SCOPE_COMMENT (line: 5, col: 14): Comment is invalid in this scope Error: WRONG_SCOPE_COMMENT (line: 5, col: 63): Comment is invalid in this scope