mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-18 03:42:23 -07:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
+193
-91
@@ -1,10 +1,28 @@
|
||||
#include "parser_pos.cpp"
|
||||
|
||||
gb_internal bool in_vet_packages(AstFile *file) {
|
||||
if (file == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (file->pkg == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (build_context.vet_packages.entries.count == 0) {
|
||||
return true;
|
||||
}
|
||||
return string_set_exists(&build_context.vet_packages, file->pkg->name);
|
||||
}
|
||||
|
||||
gb_internal u64 ast_file_vet_flags(AstFile *f) {
|
||||
if (f != nullptr && f->vet_flags_set) {
|
||||
return f->vet_flags;
|
||||
}
|
||||
return build_context.vet_flags;
|
||||
|
||||
bool found = in_vet_packages(f);
|
||||
if (found) {
|
||||
return build_context.vet_flags;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
gb_internal bool ast_file_vet_style(AstFile *f) {
|
||||
@@ -430,7 +448,8 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
|
||||
n->StructType.fields = clone_ast_array(n->StructType.fields, f);
|
||||
n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params, f);
|
||||
n->StructType.align = clone_ast(n->StructType.align, f);
|
||||
n->StructType.field_align = clone_ast(n->StructType.field_align, f);
|
||||
n->StructType.min_field_align = clone_ast(n->StructType.min_field_align, f);
|
||||
n->StructType.max_field_align = clone_ast(n->StructType.max_field_align, f);
|
||||
n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f);
|
||||
break;
|
||||
case Ast_UnionType:
|
||||
@@ -1199,7 +1218,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) {
|
||||
|
||||
gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count,
|
||||
Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy,
|
||||
Ast *align, Ast *field_align,
|
||||
Ast *align, Ast *min_field_align, Ast *max_field_align,
|
||||
Token where_token, Array<Ast *> const &where_clauses) {
|
||||
Ast *result = alloc_ast_node(f, Ast_StructType);
|
||||
result->StructType.token = token;
|
||||
@@ -1210,7 +1229,8 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i
|
||||
result->StructType.is_raw_union = is_raw_union;
|
||||
result->StructType.is_no_copy = is_no_copy;
|
||||
result->StructType.align = align;
|
||||
result->StructType.field_align = field_align;
|
||||
result->StructType.min_field_align = min_field_align;
|
||||
result->StructType.max_field_align = max_field_align;
|
||||
result->StructType.where_token = where_token;
|
||||
result->StructType.where_clauses = slice_from_array(where_clauses);
|
||||
return result;
|
||||
@@ -2739,7 +2759,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
bool is_raw_union = false;
|
||||
bool no_copy = false;
|
||||
Ast *align = nullptr;
|
||||
Ast *field_align = nullptr;
|
||||
Ast *min_field_align = nullptr;
|
||||
Ast *max_field_align = nullptr;
|
||||
|
||||
if (allow_token(f, Token_OpenParen)) {
|
||||
isize param_count = 0;
|
||||
@@ -2777,18 +2798,43 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
gb_string_free(s);
|
||||
}
|
||||
} else if (tag.string == "field_align") {
|
||||
if (field_align) {
|
||||
if (min_field_align) {
|
||||
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
|
||||
}
|
||||
field_align = parse_expr(f, true);
|
||||
if (field_align && field_align->kind != Ast_ParenExpr) {
|
||||
syntax_warning(tag, "#field_align has been deprecated in favour of #min_field_align");
|
||||
min_field_align = parse_expr(f, true);
|
||||
if (min_field_align && min_field_align->kind != Ast_ParenExpr) {
|
||||
ERROR_BLOCK();
|
||||
gbString s = expr_to_string(field_align);
|
||||
gbString s = expr_to_string(min_field_align);
|
||||
syntax_warning(tag, "#field_align requires parentheses around the expression");
|
||||
error_line("\tSuggestion: #field_align(%s)", s);
|
||||
error_line("\tSuggestion: #min_field_align(%s)", s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
} else if (tag.string == "raw_union") {
|
||||
} else if (tag.string == "min_field_align") {
|
||||
if (min_field_align) {
|
||||
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
|
||||
}
|
||||
min_field_align = parse_expr(f, true);
|
||||
if (min_field_align && min_field_align->kind != Ast_ParenExpr) {
|
||||
ERROR_BLOCK();
|
||||
gbString s = expr_to_string(min_field_align);
|
||||
syntax_warning(tag, "#min_field_align requires parentheses around the expression");
|
||||
error_line("\tSuggestion: #min_field_align(%s)", s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
} else if (tag.string == "max_field_align") {
|
||||
if (max_field_align) {
|
||||
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
|
||||
}
|
||||
max_field_align = parse_expr(f, true);
|
||||
if (max_field_align && max_field_align->kind != Ast_ParenExpr) {
|
||||
ERROR_BLOCK();
|
||||
gbString s = expr_to_string(max_field_align);
|
||||
syntax_warning(tag, "#max_field_align requires parentheses around the expression");
|
||||
error_line("\tSuggestion: #max_field_align(%s)", s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
}else if (tag.string == "raw_union") {
|
||||
if (is_raw_union) {
|
||||
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
|
||||
}
|
||||
@@ -2838,7 +2884,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
|
||||
parser_check_polymorphic_record_parameters(f, polymorphic_params);
|
||||
|
||||
return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, field_align, where_token, where_clauses);
|
||||
return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, min_field_align, max_field_align, where_token, where_clauses);
|
||||
} break;
|
||||
|
||||
case Token_union: {
|
||||
@@ -4359,10 +4405,14 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
|
||||
}
|
||||
}
|
||||
|
||||
allow_field_separator(f);
|
||||
bool more_fields = allow_field_separator(f);
|
||||
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
|
||||
array_add(¶ms, param);
|
||||
|
||||
if (!more_fields) {
|
||||
if (name_count_) *name_count_ = total_name_count;
|
||||
return ast_field_list(f, start_token, params);
|
||||
}
|
||||
|
||||
while (f->curr_token.kind != follow &&
|
||||
f->curr_token.kind != Token_EOF &&
|
||||
@@ -4949,7 +4999,7 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
|
||||
}
|
||||
|
||||
if (f->in_when_statement) {
|
||||
syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '//+build' tags");
|
||||
syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '#+build' tags");
|
||||
}
|
||||
|
||||
if (kind != ImportDecl_Standard) {
|
||||
@@ -5337,6 +5387,12 @@ gb_internal Ast *parse_stmt(AstFile *f) {
|
||||
s = ast_empty_stmt(f, token);
|
||||
expect_semicolon(f);
|
||||
return s;
|
||||
|
||||
case Token_FileTag:
|
||||
// This is always an error because all valid file tags will have been processed in `parse_file` already.
|
||||
// Any remaining file tags must be past the package line and thus invalid.
|
||||
syntax_error(token, "Lines starting with #+ (file tags) are only allowed before the package line.");
|
||||
return ast_bad_stmt(f, token, f->curr_token);
|
||||
}
|
||||
|
||||
// Error correction statements
|
||||
@@ -5372,18 +5428,9 @@ gb_internal Ast *parse_stmt(AstFile *f) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal u64 check_vet_flags(AstFile *file) {
|
||||
if (file && file->vet_flags_set) {
|
||||
return file->vet_flags;
|
||||
}
|
||||
return build_context.vet_flags;
|
||||
}
|
||||
|
||||
|
||||
gb_internal void parse_enforce_tabs(AstFile *f) {
|
||||
// Checks to see if tabs have been used for indentation
|
||||
if ((check_vet_flags(f) & VetFlag_Tabs) == 0) {
|
||||
if ((ast_file_vet_flags(f) & VetFlag_Tabs) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5969,20 +6016,6 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
|
||||
do_error(node, "Unknown library collection: '%.*s'", LIT(collection_name));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
// @NOTE(vassvik): foreign imports of shared libraries that are not in the system collection on
|
||||
// linux/mac have to be local to the executable for consistency with shared libraries.
|
||||
// Unix does not have a concept of "import library" for shared/dynamic libraries,
|
||||
// so we need to pass the relative path to the linker, and add the current
|
||||
// working directory of the exe to the library search paths.
|
||||
// Static libraries can be linked directly with the full pathname
|
||||
//
|
||||
if (node->kind == Ast_ForeignImportDecl && (string_ends_with(file_str, str_lit(".so")) || string_contains_string(file_str, str_lit(".so.")))) {
|
||||
*path = file_str;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (is_package_name_reserved(file_str)) {
|
||||
@@ -6128,7 +6161,7 @@ gb_internal String build_tag_get_token(String s, String *out) {
|
||||
}
|
||||
|
||||
gb_internal bool parse_build_tag(Token token_for_pos, String s) {
|
||||
String const prefix = str_lit("+build");
|
||||
String const prefix = str_lit("build");
|
||||
GB_ASSERT(string_starts_with(s, prefix));
|
||||
s = string_trim_whitespace(substring(s, prefix.len, s.len));
|
||||
|
||||
@@ -6141,6 +6174,10 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
|
||||
while (s.len > 0) {
|
||||
bool this_kind_correct = true;
|
||||
|
||||
bool this_kind_os_seen = false;
|
||||
bool this_kind_arch_seen = false;
|
||||
int num_tokens = 0;
|
||||
|
||||
do {
|
||||
String p = string_trim_whitespace(build_tag_get_token(s, &s));
|
||||
if (p.len == 0) break;
|
||||
@@ -6166,7 +6203,18 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
|
||||
|
||||
TargetOsKind os = get_target_os_from_string(p);
|
||||
TargetArchKind arch = get_target_arch_from_string(p);
|
||||
num_tokens += 1;
|
||||
|
||||
// Catches 'windows linux', which is an impossible combination.
|
||||
// Also catches usage of more than two things within a comma separated group.
|
||||
if (num_tokens > 2 || (this_kind_os_seen && os != TargetOs_Invalid) || (this_kind_arch_seen && arch != TargetArch_Invalid)) {
|
||||
syntax_error(token_for_pos, "Invalid build tag: Missing ',' before '%.*s'. Format: '#+build linux, windows amd64, darwin'", LIT(p));
|
||||
break;
|
||||
}
|
||||
|
||||
if (os != TargetOs_Invalid) {
|
||||
this_kind_os_seen = true;
|
||||
|
||||
GB_ASSERT(arch == TargetArch_Invalid);
|
||||
if (is_notted) {
|
||||
this_kind_correct = this_kind_correct && (os != build_context.metrics.os);
|
||||
@@ -6174,6 +6222,8 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
|
||||
this_kind_correct = this_kind_correct && (os == build_context.metrics.os);
|
||||
}
|
||||
} else if (arch != TargetArch_Invalid) {
|
||||
this_kind_arch_seen = true;
|
||||
|
||||
if (is_notted) {
|
||||
this_kind_correct = this_kind_correct && (arch != build_context.metrics.arch);
|
||||
} else {
|
||||
@@ -6213,7 +6263,7 @@ gb_internal String vet_tag_get_token(String s, String *out) {
|
||||
|
||||
|
||||
gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
|
||||
String const prefix = str_lit("+vet");
|
||||
String const prefix = str_lit("vet");
|
||||
GB_ASSERT(string_starts_with(s, prefix));
|
||||
s = string_trim_whitespace(substring(s, prefix.len, s.len));
|
||||
|
||||
@@ -6318,7 +6368,7 @@ gb_internal isize calc_decl_count(Ast *decl) {
|
||||
}
|
||||
|
||||
gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s) {
|
||||
String const prefix = str_lit("+build-project-name");
|
||||
String const prefix = str_lit("build-project-name");
|
||||
GB_ASSERT(string_starts_with(s, prefix));
|
||||
s = string_trim_whitespace(substring(s, prefix.len, s.len));
|
||||
if (s.len == 0) {
|
||||
@@ -6362,6 +6412,48 @@ gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s
|
||||
return any_correct;
|
||||
}
|
||||
|
||||
gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f) {
|
||||
if (string_starts_with(lc, str_lit("build-project-name"))) {
|
||||
if (!parse_build_project_directory_tag(tok, lc)) {
|
||||
return false;
|
||||
}
|
||||
} else if (string_starts_with(lc, str_lit("build"))) {
|
||||
if (!parse_build_tag(tok, lc)) {
|
||||
return false;
|
||||
}
|
||||
} else if (string_starts_with(lc, str_lit("vet"))) {
|
||||
f->vet_flags = parse_vet_tag(tok, lc);
|
||||
f->vet_flags_set = true;
|
||||
} else if (string_starts_with(lc, str_lit("ignore"))) {
|
||||
return false;
|
||||
} else if (string_starts_with(lc, str_lit("private"))) {
|
||||
f->flags |= AstFile_IsPrivatePkg;
|
||||
String command = string_trim_starts_with(lc, str_lit("private "));
|
||||
command = string_trim_whitespace(command);
|
||||
if (lc == "private") {
|
||||
f->flags |= AstFile_IsPrivatePkg;
|
||||
} else if (command == "package") {
|
||||
f->flags |= AstFile_IsPrivatePkg;
|
||||
} else if (command == "file") {
|
||||
f->flags |= AstFile_IsPrivateFile;
|
||||
}
|
||||
} else if (lc == "lazy") {
|
||||
if (build_context.ignore_lazy) {
|
||||
// Ignore
|
||||
} else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
|
||||
// Ignore
|
||||
} else {
|
||||
f->flags |= AstFile_IsLazy;
|
||||
}
|
||||
} else if (lc == "no-instrumentation") {
|
||||
f->flags |= AstFile_NoInstrumentation;
|
||||
} else {
|
||||
error(tok, "Unknown tag '%.*s'", LIT(lc));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_internal bool parse_file(Parser *p, AstFile *f) {
|
||||
if (f->tokens.count == 0) {
|
||||
return true;
|
||||
@@ -6380,9 +6472,34 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
|
||||
|
||||
CommentGroup *docs = f->lead_comment;
|
||||
|
||||
Array<Token> tags = array_make<Token>(temporary_allocator());
|
||||
bool first_invalid_token_set = false;
|
||||
Token first_invalid_token = {};
|
||||
|
||||
while (f->curr_token.kind != Token_package && f->curr_token.kind != Token_EOF) {
|
||||
if (f->curr_token.kind == Token_Comment) {
|
||||
consume_comment_groups(f, f->prev_token);
|
||||
} else if (f->curr_token.kind == Token_FileTag) {
|
||||
array_add(&tags, f->curr_token);
|
||||
advance_token(f);
|
||||
} else {
|
||||
if (!first_invalid_token_set) {
|
||||
first_invalid_token_set = true;
|
||||
first_invalid_token = f->curr_token;
|
||||
}
|
||||
|
||||
advance_token(f);
|
||||
}
|
||||
}
|
||||
|
||||
if (f->curr_token.kind != Token_package) {
|
||||
ERROR_BLOCK();
|
||||
syntax_error(f->curr_token, "Expected a package declaration at the beginning of the file");
|
||||
|
||||
// The while loop above scanned until it found the package token. If we never
|
||||
// found one, then make this error appear on the first invalid token line.
|
||||
Token t = first_invalid_token_set ? first_invalid_token : f->curr_token;
|
||||
syntax_error(t, "Expected a package declaration at the beginning of the file");
|
||||
|
||||
// IMPORTANT NOTE(bill): this is technically a race condition with the suggestion, but it's ony a suggession
|
||||
// so in practice is should be "fine"
|
||||
if (f->pkg && f->pkg->name != "") {
|
||||
@@ -6391,18 +6508,16 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// There was an OK package declaration. But there some invalid token was hit before the package declaration.
|
||||
if (first_invalid_token_set) {
|
||||
syntax_error(first_invalid_token, "Expected only comments or lines starting with '#+' before the package declaration");
|
||||
return false;
|
||||
}
|
||||
|
||||
f->package_token = expect_token(f, Token_package);
|
||||
if (f->package_token.kind != Token_package) {
|
||||
return false;
|
||||
}
|
||||
if (docs != nullptr) {
|
||||
TokenPos end = token_pos_end(docs->list[docs->list.count-1]);
|
||||
if (end.line == f->package_token.pos.line || end.line+1 == f->package_token.pos.line) {
|
||||
// Okay
|
||||
} else {
|
||||
docs = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Token package_name = expect_token_after(f, Token_Ident, "package");
|
||||
if (package_name.kind == Token_Ident) {
|
||||
@@ -6416,53 +6531,40 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
|
||||
}
|
||||
f->package_name = package_name.string;
|
||||
|
||||
if (!f->pkg->is_single_file && docs != nullptr && docs->list.count > 0) {
|
||||
for (Token const &tok : docs->list) {
|
||||
GB_ASSERT(tok.kind == Token_Comment);
|
||||
String str = tok.string;
|
||||
if (string_starts_with(str, str_lit("//"))) {
|
||||
// TODO: Shouldn't single file only matter for build tags? no-instrumentation for example
|
||||
// should be respected even when in single file mode.
|
||||
if (!f->pkg->is_single_file) {
|
||||
if (docs != nullptr && docs->list.count > 0) {
|
||||
for (Token const &tok : docs->list) {
|
||||
GB_ASSERT(tok.kind == Token_Comment);
|
||||
String str = tok.string;
|
||||
|
||||
if (!string_starts_with(str, str_lit("//"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String lc = string_trim_whitespace(substring(str, 2, str.len));
|
||||
if (lc.len > 0 && lc[0] == '+') {
|
||||
if (string_starts_with(lc, str_lit("+build-project-name"))) {
|
||||
if (!parse_build_project_directory_tag(tok, lc)) {
|
||||
return false;
|
||||
}
|
||||
} else if (string_starts_with(lc, str_lit("+build"))) {
|
||||
if (!parse_build_tag(tok, lc)) {
|
||||
return false;
|
||||
}
|
||||
} else if (string_starts_with(lc, str_lit("+vet"))) {
|
||||
f->vet_flags = parse_vet_tag(tok, lc);
|
||||
f->vet_flags_set = true;
|
||||
} else if (string_starts_with(lc, str_lit("+ignore"))) {
|
||||
if (string_starts_with(lc, str_lit("+"))) {
|
||||
syntax_warning(tok, "'//+' is deprecated: Use '#+' instead");
|
||||
String lt = substring(lc, 1, lc.len);
|
||||
if (parse_file_tag(lt, tok, f) == false) {
|
||||
return false;
|
||||
} else if (string_starts_with(lc, str_lit("+private"))) {
|
||||
f->flags |= AstFile_IsPrivatePkg;
|
||||
String command = string_trim_starts_with(lc, str_lit("+private "));
|
||||
command = string_trim_whitespace(command);
|
||||
if (lc == "+private") {
|
||||
f->flags |= AstFile_IsPrivatePkg;
|
||||
} else if (command == "package") {
|
||||
f->flags |= AstFile_IsPrivatePkg;
|
||||
} else if (command == "file") {
|
||||
f->flags |= AstFile_IsPrivateFile;
|
||||
}
|
||||
} else if (lc == "+lazy") {
|
||||
if (build_context.ignore_lazy) {
|
||||
// Ignore
|
||||
} else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
|
||||
// Ignore
|
||||
} else {
|
||||
f->flags |= AstFile_IsLazy;
|
||||
}
|
||||
} else if (lc == "+no-instrumentation") {
|
||||
f->flags |= AstFile_NoInstrumentation;
|
||||
} else {
|
||||
warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Token const &tok : tags) {
|
||||
GB_ASSERT(tok.kind == Token_FileTag);
|
||||
String str = tok.string;
|
||||
|
||||
if (string_starts_with(str, str_lit("#+"))) {
|
||||
String lt = string_trim_whitespace(substring(str, 2, str.len));
|
||||
if (parse_file_tag(lt, tok, f) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ast *pd = ast_package_decl(f, f->package_token, package_name, docs, f->line_comment);
|
||||
|
||||
Reference in New Issue
Block a user