Skip to content

Commit

Permalink
feat: add to allow use comments in other scopes that is not from a fu…
Browse files Browse the repository at this point in the history
…nction (#454)

* feat: add to allow use comments in other scopes that is not from a function

* feat: add tests for comments in diff scopes

* fix: add errors that appeared in 'test_comments.c' after merge
  • Loading branch information
NiumXp committed Nov 20, 2023
1 parent 41442cc commit a08d048
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 33 deletions.
2 changes: 1 addition & 1 deletion norminette/norm_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
72 changes: 47 additions & 25 deletions norminette/rules/check_comment.py
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 1 addition & 1 deletion norminette/rules/check_func_arguments_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
21 changes: 21 additions & 0 deletions tests/rules/samples/test_comments.c
Original file line number Diff line number Diff line change
@@ -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
}
}
75 changes: 75 additions & 0 deletions tests/rules/samples/test_comments.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
test_comments.c - IsUserDefinedType In "GlobalScope" from "None" line 1":
<STRUCT> <SPACE> <LBRACE> <NEWLINE>
test_comments.c - IsComment In "UserDefinedType" from "GlobalScope" line 2":
<TAB> <COMMENT=// points is to something> <NEWLINE>
test_comments.c - IsVarDeclaration In "UserDefinedType" from "GlobalScope" line 3":
<TAB> <INT> <TAB> <IDENTIFIER=points> <SEMI_COLON> <SPACE>
test_comments.c - IsComment In "UserDefinedType" from "GlobalScope" line 3":
<COMMENT=// is an int :D> <NEWLINE>
test_comments.c - IsBlockEnd In "UserDefinedType" from "GlobalScope" line 4":
<RBRACE> <SEMI_COLON> <NEWLINE>
test_comments.c - IsEmptyLine In "GlobalScope" from "None" line 5":
<NEWLINE>
test_comments.c - IsUserDefinedType In "GlobalScope" from "None" line 6":
<TYPEDEF> <SPACE> <MULT_COMMENT=/* oopss */> <SPACE> <IDENTIFIER=bool> <SPACE> <IDENTIFIER=bool> <SEMI_COLON> <NEWLINE>
test_comments.c - IsEmptyLine In "GlobalScope" from "None" line 7":
<NEWLINE>
test_comments.c - IsUserDefinedType In "GlobalScope" from "None" line 8":
<ENUM> <SPACE> <IDENTIFIER=test> <SPACE> <LBRACE> <NEWLINE>
test_comments.c - IsComment In "UserDefinedEnum" from "GlobalScope" line 9":
<SPACE> <SPACE> <SPACE> <SPACE> <COMMENT=// blaboe> <NEWLINE>
test_comments.c - IsEnumVarDecl In "UserDefinedEnum" from "GlobalScope" line 10":
<SPACE> <SPACE> <SPACE> <SPACE> <IDENTIFIER=hello> <COMMA> <SPACE> <SPACE>
test_comments.c - IsComment In "UserDefinedEnum" from "GlobalScope" line 10":
<COMMENT=// it works> <NEWLINE>
test_comments.c - IsComment In "UserDefinedEnum" from "GlobalScope" line 11":
<SPACE> <SPACE> <SPACE> <SPACE> <MULT_COMMENT=/* error*/> <SPACE>
test_comments.c - IsEnumVarDecl In "UserDefinedEnum" from "GlobalScope" line 11":
<IDENTIFIER=error> <NEWLINE>
test_comments.c - IsBlockEnd In "UserDefinedEnum" from "GlobalScope" line 12":
<RBRACE> <SEMI_COLON> <NEWLINE>
test_comments.c - IsEmptyLine In "GlobalScope" from "None" line 13":
<NEWLINE>
test_comments.c - IsFuncDeclaration In "GlobalScope" from "None" line 14":
<VOID> <SPACE> <IDENTIFIER=hello> <LPARENTHESIS> <MULT_COMMENT=/* nothing */> <SPACE> <VOID> <RPARENTHESIS> <SPACE> <COMMENT=// error because comment is in middle of the line> <NEWLINE>
test_comments.c - IsBlockStart In "Function" from "GlobalScope" line 15":
<LBRACE> <NEWLINE>
test_comments.c - IsComment In "Function" from "GlobalScope" line 16":
<SPACE> <SPACE> <SPACE> <COMMENT=// error because scope is from a function> <NEWLINE>
test_comments.c - IsBlockStart In "Function" from "GlobalScope" line 17":
<SPACE> <SPACE> <SPACE> <LBRACE> <NEWLINE>
test_comments.c - IsComment In "ControlStructure" from "Function" line 18":
<SPACE> <SPACE> <SPACE> <SPACE> <SPACE> <SPACE> <COMMENT=// are you trying to cheat?> <NEWLINE>
test_comments.c - IsComment In "ControlStructure" from "Function" line 19":
<SPACE> <SPACE> <SPACE> <SPACE> <SPACE> <SPACE> <COMMENT=// error because scope is from a function> <NEWLINE>
test_comments.c - IsBlockEnd In "ControlStructure" from "Function" line 20":
<SPACE> <SPACE> <SPACE> <RBRACE> <NEWLINE>
test_comments.c - IsBlockEnd In "Function" from "GlobalScope" line 21":
<RBRACE> <NEWLINE>
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
10 changes: 5 additions & 5 deletions tests/rules/samples/test_file_1019.out
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion tests/rules/samples/test_file_210128.out
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
<RBRACE> <NEWLINE>
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
Expand Down

0 comments on commit a08d048

Please sign in to comment.