mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Function comments are stored and displayed with -P.
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
- Improve the error message when running out of memory.
|
||||
- Allowed passing arguments to @test / @benchmark runners via `c3c test[benchmark] -- -o --opt1 <arg1>`
|
||||
- Handle bytes and string literals the same way in terms of zero termination.
|
||||
- Function comments are stored and displayed with -P.
|
||||
|
||||
### Fixes
|
||||
- Fix case trying to initialize a `char[*]*` from a String.
|
||||
|
||||
@@ -1473,6 +1473,11 @@ typedef struct AstDocDirective_
|
||||
const char *directive_name;
|
||||
const char *rest_of_line;
|
||||
} generic;
|
||||
struct
|
||||
{
|
||||
const char *string;
|
||||
size_t strlen;
|
||||
};
|
||||
};
|
||||
} AstContractStmt;
|
||||
|
||||
|
||||
@@ -615,6 +615,7 @@ void doc_ast_copy(CopyStruct *c, AstContractStmt *doc)
|
||||
case CONTRACT_PARAM:
|
||||
case CONTRACT_PURE:
|
||||
case CONTRACT_UNKNOWN:
|
||||
case CONTRACT_COMMENT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,6 +594,7 @@ typedef enum
|
||||
typedef enum
|
||||
{
|
||||
CONTRACT_UNKNOWN,
|
||||
CONTRACT_COMMENT,
|
||||
CONTRACT_PURE,
|
||||
CONTRACT_REQUIRE,
|
||||
CONTRACT_PARAM,
|
||||
|
||||
@@ -10,21 +10,63 @@
|
||||
unsigned decl_count_ = vec_size(unit->global_decls); \
|
||||
for (unsigned k_ = 0; k_ < decl_count_; k_++) { \
|
||||
a__ = unit->global_decls[k_];
|
||||
#define PRINTF(string, ...) fprintf(file, string, ##__VA_ARGS__) /* NOLINT */
|
||||
#define PRINTF(string__, ...) fprintf(file, string__, ##__VA_ARGS__) /* NOLINT */
|
||||
#define PRINT(string__) fputs(string__, file) /* NOLINT */
|
||||
#define FOREACH_DECL_END } } }
|
||||
#define INSERT_COMMA do { if (first) { first = false; } else { fputs(",\n", file); } } while(0)
|
||||
|
||||
static bool emit_docs(FILE *file, AstId contracts, int tabs)
|
||||
{
|
||||
if (!contracts) return false;
|
||||
Ast *ast = astptr(contracts);
|
||||
if (ast->contract_stmt.kind != CONTRACT_COMMENT) return false;
|
||||
for (int i = 0; i < tabs; i++) PRINT("\t");
|
||||
PRINT("\"comment\": \"");
|
||||
bool last_is_whitespace = true;
|
||||
for (size_t i = 0; i < ast->contract_stmt.strlen; i++)
|
||||
{
|
||||
unsigned char c = ast->contract_stmt.string[i];
|
||||
if (char_is_whitespace(c) || c < 31)
|
||||
{
|
||||
if (last_is_whitespace) continue;
|
||||
fputc(' ', file);
|
||||
last_is_whitespace = true;
|
||||
continue;
|
||||
}
|
||||
last_is_whitespace = false;
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
PRINT("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
PRINT("\\\\");
|
||||
break;
|
||||
default:
|
||||
if (c > 127)
|
||||
{
|
||||
PRINTF("\\x%02x", c);
|
||||
}
|
||||
else
|
||||
{
|
||||
fputc(c, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
PRINT("\"");
|
||||
return true;
|
||||
}
|
||||
static inline void emit_modules(FILE *file)
|
||||
{
|
||||
|
||||
fputs("\t\"modules\": [\n", file);
|
||||
PRINT("\t\"modules\": [\n");
|
||||
FOREACH_IDX(i, Module *, module, compiler.context.module_list)
|
||||
{
|
||||
if (i != 0) fputs(",\n", file);
|
||||
PRINTF("\t\t\"%s\"", module->name->module);
|
||||
}
|
||||
fputs("\n\t],\n", file);
|
||||
fputs("\t\"generic_modules\": [\n", file);
|
||||
PRINT("\n\t],\n");
|
||||
PRINT("\t\"generic_modules\": [\n");
|
||||
FOREACH_IDX(j, Module *, module, compiler.context.generic_module_list)
|
||||
{
|
||||
if (j != 0) fputs(",\n", file);
|
||||
@@ -192,45 +234,83 @@ void print_var_expr(FILE *file, Expr *expr)
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void print_indent(FILE *file, int indent)
|
||||
{
|
||||
for (int j = 0; j < indent; j++) PRINT("\t");
|
||||
}
|
||||
static inline void emit_members(FILE *file, Decl **members, int indent)
|
||||
{
|
||||
FOREACH_IDX(i, Decl *, member, members)
|
||||
{
|
||||
if (i != 0) fputs(",\n", file);
|
||||
print_indent(file, indent);
|
||||
PRINTF("\t\t\t\t{\n");
|
||||
if (member->name)
|
||||
{
|
||||
print_indent(file, indent);
|
||||
PRINTF("\t\t\t\t\t\"name\": \"%s\",\n", member->name);
|
||||
}
|
||||
if (member->decl_kind == DECL_VAR)
|
||||
{
|
||||
print_indent(file, indent);
|
||||
PRINTF("\t\t\t\t\t\"type\": \"");
|
||||
ASSERT0(member->var.type_info);
|
||||
print_type(file, type_infoptr(member->var.type_info));
|
||||
PRINT("\"\n");
|
||||
print_indent(file, indent);
|
||||
PRINTF("\t\t\t\t}");
|
||||
continue;
|
||||
}
|
||||
print_indent(file, indent);
|
||||
PRINTF("\t\t\t\t\t\"inner\": ");
|
||||
PRINT(member->decl_kind == DECL_STRUCT ? "\"struct\"" : "\"union\"");
|
||||
PRINT(",\n");
|
||||
print_indent(file, indent);
|
||||
PRINT("\t\t\t\t\t\"members\": [\n");
|
||||
emit_members(file, member->strukt.members, indent + 2);
|
||||
PRINT("\n");
|
||||
print_indent(file, indent);
|
||||
PRINT("\t\t\t\t\t]\n");
|
||||
print_indent(file, indent);
|
||||
PRINTF("\t\t\t\t}");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
static inline void emit_type_data(FILE *file, Module *module, Decl *type)
|
||||
{
|
||||
PRINTF("\t\t\"%s::%s\": {\n", module->name->module, type->name);
|
||||
PRINTF("\t\t\t\"kind\": \"%s\"", decl_type_to_string(type));
|
||||
if (type->decl_kind == DECL_STRUCT || type->decl_kind == DECL_UNION)
|
||||
switch (type->decl_kind)
|
||||
{
|
||||
fputs(",\n\t\t\t\"members\": [\n", file);
|
||||
FOREACH_IDX(i, Decl *, member, type->strukt.members)
|
||||
{
|
||||
if (i != 0) fputs(",\n", file);
|
||||
PRINTF("\t\t\t\t{\n");
|
||||
if (member->name)
|
||||
{
|
||||
PRINTF("\t\t\t\t\t\"name\": \"%s\",\n", member->name);
|
||||
}
|
||||
// TODO, extend this
|
||||
PRINTF("\t\t\t\t\t\"type\": \"");
|
||||
if (member->var.type_info)
|
||||
{
|
||||
print_type(file, type_infoptr(member->var.type_info));
|
||||
}
|
||||
else
|
||||
{
|
||||
fputs("", file);
|
||||
}
|
||||
PRINTF("\"\n\t\t\t\t}");
|
||||
}
|
||||
fputs("\n\t\t\t]", file);
|
||||
case DECL_UNION:
|
||||
case DECL_STRUCT:
|
||||
fputs(",\n\t\t\t\"members\": [\n", file);
|
||||
emit_members(file, type->strukt.members, 0);
|
||||
fputs("\n\t\t\t]", file);
|
||||
break;
|
||||
case DECL_DISTINCT:
|
||||
PRINT(",\n\t\t\t\"type\": \"");
|
||||
print_type(file, type->distinct);
|
||||
PRINTF(",\n\t\t\t\"inline\": \"%s\"", type->is_substruct ? "true" : "false");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fputs("\n\t\t}", file);
|
||||
PRINT("\n\t\t}");
|
||||
}
|
||||
|
||||
static inline void emit_func_data(FILE *file, Module *module, Decl *func)
|
||||
{
|
||||
PRINTF("\t\t\"%s::%s\": {\n", module->name->module, func->name);
|
||||
if (emit_docs(file, func->func_decl.docs, 3))
|
||||
{
|
||||
PRINTF(",\n");
|
||||
}
|
||||
PRINTF("\t\t\t\"rtype\": \"");
|
||||
print_type(file, type_infoptr(func->func_decl.signature.rtype));
|
||||
PRINTF("\",\n");
|
||||
fputs("\t\t\t\"params\": [\n", file);
|
||||
PRINT("\t\t\t\"params\": [\n");
|
||||
FOREACH_IDX(i, Decl *, decl, func->func_decl.signature.params)
|
||||
{
|
||||
if (!decl) continue;
|
||||
@@ -254,6 +334,43 @@ static inline void emit_func_data(FILE *file, Module *module, Decl *func)
|
||||
fputs("\n\t\t}", file);
|
||||
}
|
||||
|
||||
static inline void emit_macro_data(FILE *file, Module *module, Decl *macro)
|
||||
{
|
||||
PRINTF("\t\t\"%s::%s\": {\n", module->name->module, macro->name);
|
||||
if (emit_docs(file, macro->func_decl.docs, 3))
|
||||
{
|
||||
PRINTF(",\n");
|
||||
}
|
||||
if (macro->func_decl.signature.rtype)
|
||||
{
|
||||
PRINTF("\t\t\t\"rtype\": \"");
|
||||
print_type(file, type_infoptr(macro->func_decl.signature.rtype));
|
||||
PRINTF("\",\n");
|
||||
}
|
||||
fputs("\t\t\t\"params\": [\n", file);
|
||||
FOREACH_IDX(i, Decl *, decl, macro->func_decl.signature.params)
|
||||
{
|
||||
if (!decl) continue;
|
||||
if (i != 0) fputs(",\n", file);
|
||||
fputs("\t\t\t\t{\n", file);
|
||||
PRINTF("\t\t\t\t\t\"name\": \"%s\",\n", decl->name ? decl->name : "");
|
||||
PRINTF("\t\t\t\t\t\"type\": \"");
|
||||
if (decl->var.type_info)
|
||||
{
|
||||
print_type(file, type_infoptr(decl->var.type_info));
|
||||
}
|
||||
else
|
||||
{
|
||||
fputs("", file);
|
||||
}
|
||||
fputs("\"\n", file);
|
||||
fputs("\t\t\t\t}", file);
|
||||
}
|
||||
fputs("\n\t\t\t]\n", file);
|
||||
|
||||
fputs("\n\t\t}", file);
|
||||
}
|
||||
|
||||
static inline bool decl_is_hidden(Decl *decl)
|
||||
{
|
||||
return decl->visibility > VISIBLE_PUBLIC;
|
||||
@@ -355,6 +472,17 @@ static inline void emit_functions(FILE *file)
|
||||
FOREACH_DECL_END;
|
||||
}
|
||||
fputs("\n\t},\n", file);
|
||||
fputs("\t\"macros\": {\n", file);
|
||||
{
|
||||
bool first = true;
|
||||
FOREACH_DECL(Decl *func, compiler.context.module_list)
|
||||
if (func->decl_kind != DECL_MACRO) continue;
|
||||
if (decl_is_hidden(func)) continue;
|
||||
INSERT_COMMA;
|
||||
emit_macro_data(file, module, func);
|
||||
FOREACH_DECL_END;
|
||||
}
|
||||
fputs("\n\t},\n", file);
|
||||
|
||||
fputs("\t\"generic_functions\": {\n", file);
|
||||
{
|
||||
@@ -367,6 +495,19 @@ static inline void emit_functions(FILE *file)
|
||||
FOREACH_DECL_END;
|
||||
}
|
||||
fputs("\n\t},\n", file);
|
||||
|
||||
fputs("\t\"generic_macros\": {\n", file);
|
||||
{
|
||||
bool first = true;
|
||||
FOREACH_DECL(Decl *func, compiler.context.generic_module_list)
|
||||
if (func->decl_kind != DECL_MACRO) continue;
|
||||
if (decl_is_hidden(func)) continue;
|
||||
INSERT_COMMA;
|
||||
emit_macro_data(file, module, func);
|
||||
FOREACH_DECL_END;
|
||||
}
|
||||
fputs("\n\t},\n", file);
|
||||
|
||||
}
|
||||
|
||||
static inline void emit_json_to_file(FILE *file)
|
||||
|
||||
@@ -1158,6 +1158,7 @@ static inline bool scan_base64(Lexer *lexer)
|
||||
**/
|
||||
static bool parse_doc_start(Lexer *lexer)
|
||||
{
|
||||
const char *comment_start = NULL;
|
||||
bool may_have_contract = true;
|
||||
// Let's loop until we find the end or the contract.
|
||||
while (!reached_end(lexer))
|
||||
@@ -1188,6 +1189,10 @@ static bool parse_doc_start(Lexer *lexer)
|
||||
FALLTHROUGH;
|
||||
default:
|
||||
may_have_contract = false;
|
||||
if (!comment_start)
|
||||
{
|
||||
comment_start = lexer->current;
|
||||
}
|
||||
next(lexer);
|
||||
continue;
|
||||
}
|
||||
@@ -1200,7 +1205,13 @@ EXIT:;
|
||||
//
|
||||
// In any case we can consider this having reached "the contracts"
|
||||
lexer->mode = LEX_CONTRACTS;
|
||||
return new_token(lexer, TOKEN_DOCS_START, lexer->lexing_start);
|
||||
lexer->data.strlen = 0;
|
||||
if (!comment_start) return new_token(lexer, TOKEN_DOCS_START, "<*");
|
||||
new_token(lexer, TOKEN_DOCS_START, comment_start);
|
||||
const char *last = lexer->current - 1;
|
||||
while (last > comment_start && char_is_whitespace(*last)) last--;
|
||||
lexer->data.strlen = last - comment_start + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool lexer_scan_token_inner(Lexer *lexer)
|
||||
|
||||
@@ -246,6 +246,7 @@ bool parse_module(ParseContext *c, AstId contracts)
|
||||
case CONTRACT_ENSURE:
|
||||
break;
|
||||
case CONTRACT_REQUIRE:
|
||||
case CONTRACT_COMMENT:
|
||||
continue;
|
||||
}
|
||||
RETURN_PRINT_ERROR_AT(false, current, "Invalid constraint - only '@require' is valid for modules.");
|
||||
@@ -2677,9 +2678,21 @@ static inline bool parse_doc_optreturn(ParseContext *c, AstId *docs, AstId **doc
|
||||
static bool parse_contracts(ParseContext *c, AstId *contracts_ref)
|
||||
{
|
||||
*contracts_ref = 0;
|
||||
if (!try_consume(c, TOKEN_DOCS_START)) return true;
|
||||
if (!tok_is(c, TOKEN_DOCS_START)) return true;
|
||||
|
||||
AstId **next = &contracts_ref;
|
||||
if (c->data.strlen > 0)
|
||||
{
|
||||
Ast *ast = ast_new_curr(c, AST_CONTRACT);
|
||||
ast->contract_stmt.kind = CONTRACT_COMMENT;
|
||||
ast->contract_stmt.string = symstr(c);
|
||||
ast->contract_stmt.strlen = c->data.strlen;
|
||||
ast->span = c->span;
|
||||
append_docs(next, contracts_ref, ast);
|
||||
}
|
||||
|
||||
advance_and_verify(c, TOKEN_DOCS_START);
|
||||
|
||||
while (!try_consume(c, TOKEN_DOCS_END))
|
||||
{
|
||||
// Skip empty lines.
|
||||
@@ -2796,6 +2809,15 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref)
|
||||
if (!parse_contracts(c, &contracts)) return poisoned_decl;
|
||||
Decl *decl;
|
||||
|
||||
bool has_real_contracts = false;
|
||||
if (contracts)
|
||||
{
|
||||
Ast *contract_start = astptr(contracts);
|
||||
if (contract_start->contract_stmt.kind != CONTRACT_COMMENT || contract_start->next)
|
||||
{
|
||||
has_real_contracts = true;
|
||||
}
|
||||
}
|
||||
TokenType tok = c->tok;
|
||||
if (tok != TOKEN_MODULE && !c->unit->module)
|
||||
{
|
||||
@@ -2815,13 +2837,13 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref)
|
||||
decl = parse_func_definition(c, contracts, FUNC_PARSE_EXTERN);
|
||||
break;
|
||||
case TOKEN_CONST:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_top_level_const_declaration(c, true);
|
||||
break;
|
||||
case TOKEN_IDENT:
|
||||
case TOKEN_TLOCAL:
|
||||
case TYPELIKE_TOKENS:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_global_declaration(c);
|
||||
break;
|
||||
default:
|
||||
@@ -2852,7 +2874,7 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref)
|
||||
PRINT_ERROR_HERE("There are more than one doc comment in a row, that is not allowed.");
|
||||
return poisoned_decl;
|
||||
case TOKEN_DEF:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_def(c);
|
||||
break;
|
||||
case TOKEN_FN:
|
||||
@@ -2900,39 +2922,39 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref)
|
||||
decl = parse_exec(c);
|
||||
break;
|
||||
case TOKEN_BITSTRUCT:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_bitstruct_declaration(c);
|
||||
break;
|
||||
case TOKEN_INTERFACE:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_interface_declaration(c);
|
||||
break;
|
||||
case TOKEN_DISTINCT:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_distinct_declaration(c);
|
||||
break;
|
||||
case TOKEN_CONST:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_top_level_const_declaration(c, false);
|
||||
break;
|
||||
case TOKEN_STRUCT:
|
||||
case TOKEN_UNION:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_struct_declaration(c);
|
||||
break;
|
||||
case TOKEN_MACRO:
|
||||
decl = parse_macro_declaration(c, contracts);
|
||||
break;
|
||||
case TOKEN_ENUM:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_enum_declaration(c);
|
||||
break;
|
||||
case TOKEN_FAULT:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_fault_declaration(c);
|
||||
break;
|
||||
case TOKEN_IDENT:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_global_declaration(c);
|
||||
break;
|
||||
case TOKEN_EOF:
|
||||
@@ -2953,7 +2975,7 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref)
|
||||
return poisoned_decl;
|
||||
case TOKEN_TLOCAL:
|
||||
case TYPELIKE_TOKENS:
|
||||
if (contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
if (has_real_contracts) goto CONTRACT_NOT_ALLOWED;
|
||||
decl = parse_global_declaration(c);
|
||||
break;
|
||||
case TOKEN_EOS:
|
||||
|
||||
@@ -3179,6 +3179,7 @@ bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, So
|
||||
{
|
||||
case CONTRACT_UNKNOWN:
|
||||
case CONTRACT_PURE:
|
||||
case CONTRACT_COMMENT:
|
||||
break;
|
||||
case CONTRACT_REQUIRE:
|
||||
if (!sema_analyse_require(context, directive, asserts, call_span)) return false;
|
||||
|
||||
Reference in New Issue
Block a user