aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/IR.rst3
-rw-r--r--Makefile12
-rw-r--r--builtin.c2
-rw-r--r--dissect.c96
-rw-r--r--dissect.h10
-rw-r--r--evaluate.c16
-rw-r--r--expand.c19
-rw-r--r--expression.c2
-rw-r--r--inline.c5
-rw-r--r--lib.c14
-rw-r--r--lib.h1
-rw-r--r--linearize.c52
-rw-r--r--opcode.def1
-rw-r--r--parse.c39
-rw-r--r--pre-process.c21
-rw-r--r--scope.c5
-rw-r--r--scope.h1
-rw-r--r--sindex.1151
-rw-r--r--sindex.c1164
-rw-r--r--symbol.c19
-rw-r--r--symbol.h9
-rw-r--r--test-dissect.c83
-rw-r--r--validation/autotype-ko.c45
-rw-r--r--validation/autotype.c55
-rw-r--r--validation/context-unreachable.c15
-rw-r--r--validation/expand/builtin_constant_inline0.c23
-rw-r--r--validation/expand/builtin_constant_inline1.c24
-rw-r--r--validation/inline_base0.c47
-rw-r--r--validation/inline_self.c13
-rw-r--r--validation/linear/builtin_unreachable0.c27
-rw-r--r--validation/linear/builtin_unreachable1.c (renamed from validation/linear/builtin_unreachable.c)15
-rw-r--r--validation/linear/noreturn-unreachable0.c22
-rw-r--r--validation/preprocessor/directive-within-macro.c36
-rw-r--r--validation/preprocessor/expand-and-nl.c13
-rw-r--r--validation/preprocessor/expand-redef.c20
-rw-r--r--validation/preprocessor/preprocessor22.c14
37 files changed, 1990 insertions, 105 deletions
diff --git a/.gitignore b/.gitignore
index 91305a6a..58598364 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ graph
obfuscate
sparse
sparse-llvm
+sindex
test-dissect
test-inspect
test-lexing
diff --git a/Documentation/IR.rst b/Documentation/IR.rst
index 9d6f2299..33a76166 100644
--- a/Documentation/IR.rst
+++ b/Documentation/IR.rst
@@ -47,6 +47,9 @@ Terminators
* .type: type of .cond, must be an integral type
* .multijmp_list: pairs of case-value - destination basic block
+.. op:: OP_UNREACH
+ Mark code as unreachable
+
.. op:: OP_COMPUTEDGOTO
Computed goto / branch to register
diff --git a/Makefile b/Makefile
index e2d3a7de..85d15edc 100644
--- a/Makefile
+++ b/Makefile
@@ -140,6 +140,17 @@ else
$(warning Your system does not have libxml, disabling c2xml)
endif
+HAVE_SQLITE := $(shell $(PKG_CONFIG) --exists sqlite3 2>/dev/null && echo 'yes')
+ifeq ($(HAVE_SQLITE),yes)
+PROGRAMS += sindex
+INST_PROGRAMS += sindex
+INST_MAN1 += sindex.1
+sindex-ldlibs := $(shell $(PKG_CONFIG) --libs sqlite3)
+sindex-cflags := $(shell $(PKG_CONFIG) --cflags sqlite3)
+else
+$(warning Your system does not have sqlite3, disabling sindex)
+endif
+
# Can we use gtk (needed for test-inspect)
GTK_VERSION:=3.0
HAVE_GTK:=$(shell $(PKG_CONFIG) --exists gtk+-$(GTK_VERSION) 2>/dev/null && echo 'yes')
@@ -228,7 +239,6 @@ cflags += $($(*)-cflags) $(CPPFLAGS) $(CFLAGS)
selfcheck: $(OBJS:.o=.sc)
-
SPARSE_VERSION:=$(shell git describe --dirty 2>/dev/null || echo '$(VERSION)')
lib.o: version.h
version.h: FORCE
diff --git a/builtin.c b/builtin.c
index 52285a91..debc22c0 100644
--- a/builtin.c
+++ b/builtin.c
@@ -418,6 +418,8 @@ void init_builtins(int stream)
sym->op = ptr->op;
sym->builtin = 1;
}
+
+ init_linearized_builtins(stream);
}
static void declare_builtin(const char *name, struct symbol *rtype, int variadic, ...)
diff --git a/dissect.c b/dissect.c
index 60fccbdb..1970e46f 100644
--- a/dissect.c
+++ b/dissect.c
@@ -51,8 +51,9 @@
typedef unsigned usage_t;
+struct symbol *dissect_ctx;
+
static struct reporter *reporter;
-static struct symbol *return_type;
static void do_sym_list(struct symbol_list *list);
@@ -118,24 +119,12 @@ static usage_t fix_mode(struct symbol *type, usage_t mode)
return mode;
}
-static inline struct symbol *no_member(struct ident *name)
-{
- static struct symbol sym = {
- .type = SYM_BAD,
- };
-
- sym.ctype.base_type = &bad_ctype;
- sym.ident = name ?: built_in_ident("?");
-
- return &sym;
-}
-
static struct symbol *report_member(usage_t mode, struct position *pos,
struct symbol *type, struct symbol *mem)
{
struct symbol *ret = mem->ctype.base_type;
- if (mem->ident)
+ if (mem->ident || mem->type == SYM_BAD)
reporter->r_member(fix_mode(ret, mode), pos, type, mem);
return ret;
@@ -163,7 +152,7 @@ static inline struct symbol *expr_symbol(struct expression *expr)
if (!sym) {
sym = alloc_symbol(expr->pos, SYM_BAD);
bind_symbol(sym, expr->symbol_name, NS_SYMBOL);
- sym->ctype.modifiers = MOD_EXTERN;
+ sym->kind = expr->op ?: 'v'; /* see EXPR_CALL */
}
}
@@ -205,20 +194,20 @@ static bool deanon(struct symbol *base, struct ident *node, struct symbol *paren
static void report_memdef(struct symbol *sym, struct symbol *mem)
{
+ mem->kind = 'm';
if (sym && mem->ident)
reporter->r_memdef(sym, mem);
}
static void examine_sym_node(struct symbol *node, struct symbol *parent)
{
- struct symbol *base;
- struct ident *name;
+ struct ident *name = node->ident;
+ struct symbol *base, *dctx;
if (node->examined)
return;
-
node->examined = 1;
- name = node->ident;
+ node->kind = 'v';
while ((base = node->ctype.base_type) != NULL)
switch (base->type) {
@@ -229,16 +218,27 @@ static void examine_sym_node(struct symbol *node, struct symbol *parent)
case SYM_ARRAY:
do_expression(U_R_VAL, base->array_size);
- case SYM_PTR: case SYM_FN:
+ case SYM_PTR:
+ node = base;
+ break;
+
+ case SYM_FN:
+ node->kind = 'f';
node = base;
break;
case SYM_STRUCT: case SYM_UNION: //case SYM_ENUM:
if (base->evaluated)
return;
+ base->evaluated = 1;
+ base->kind = 's';
+
if (!base->symbol_list)
return;
- base->evaluated = 1;
+
+ dctx = dissect_ctx;
+ if (toplevel(base->scope))
+ dissect_ctx = NULL;
if (base->ident || deanon(base, name, parent))
reporter->r_symdef(base);
@@ -248,6 +248,7 @@ static void examine_sym_node(struct symbol *node, struct symbol *parent)
DO_LIST(base->symbol_list, mem,
examine_sym_node(mem, parent);
report_memdef(parent, mem));
+ dissect_ctx = dctx;
default:
return;
}
@@ -294,8 +295,23 @@ found:
static struct symbol *lookup_member(struct symbol *type, struct ident *name, int *addr)
{
- return __lookup_member(type, name, addr)
- ?: no_member(name);
+ struct symbol *mem = __lookup_member(type, name, addr);
+
+ if (!mem) {
+ static struct symbol bad_member = {
+ .type = SYM_BAD,
+ .ctype.base_type = &bad_ctype,
+ .kind = 'm',
+ };
+
+ if (!type->symbol_list)
+ type->scope = file_scope;
+
+ mem = &bad_member;
+ mem->ident = name;
+ }
+
+ return mem;
}
static struct expression *peek_preop(struct expression *expr, int op)
@@ -360,6 +376,8 @@ again:
ret = do_expression(mode, expr->cond_false);
break; case EXPR_CALL:
+ if (expr->fn->type == EXPR_SYMBOL)
+ expr->fn->op = 'f'; /* for expr_symbol() */
ret = do_expression(U_R_PTR, expr->fn);
if (is_ptr(ret))
ret = ret->ctype.base_type;
@@ -483,8 +501,10 @@ static struct symbol *do_statement(usage_t mode, struct statement *stmt)
break; case STMT_EXPRESSION:
ret = do_expression(mode, stmt->expression);
- break; case STMT_RETURN:
- do_expression(u_lval(return_type), stmt->expression);
+ break; case STMT_RETURN: {
+ struct symbol *type = dissect_ctx->ctype.base_type;
+ do_expression(u_lval(base_type(type)), stmt->expression);
+ }
break; case STMT_ASM:
do_expression(U_R_VAL, stmt->asm_string);
@@ -582,6 +602,8 @@ static struct symbol *do_initializer(struct symbol *type, struct expression *exp
static inline struct symbol *do_symbol(struct symbol *sym)
{
struct symbol *type = base_type(sym);
+ struct symbol *dctx = dissect_ctx;
+ struct statement *stmt;
reporter->r_symdef(sym);
@@ -590,14 +612,25 @@ static inline struct symbol *do_symbol(struct symbol *sym)
if (!sym->initializer)
break;
reporter->r_symbol(U_W_VAL, &sym->pos, sym);
+ if (!dctx)
+ dissect_ctx = sym;
do_initializer(type, sym->initializer);
+ dissect_ctx = dctx;
break; case SYM_FN:
+ stmt = sym->ctype.modifiers & MOD_INLINE
+ ? type->inline_stmt : type->stmt;
+ if (!stmt)
+ break;
+
+ if (dctx)
+ sparse_error(dctx->pos, "dissect_ctx change %s -> %s",
+ show_ident(dctx->ident), show_ident(sym->ident));
+
+ dissect_ctx = sym;
do_sym_list(type->arguments);
- return_type = base_type(type);
- do_statement(U_VOID, sym->ctype.modifiers & MOD_INLINE
- ? type->inline_stmt
- : type->stmt);
+ do_statement(U_VOID, stmt);
+ dissect_ctx = dctx;
}
return type;
@@ -608,8 +641,9 @@ static void do_sym_list(struct symbol_list *list)
DO_LIST(list, sym, do_symbol(sym));
}
-void dissect(struct symbol_list *list, struct reporter *rep)
+void dissect(struct reporter *rep, struct string_list *filelist)
{
reporter = rep;
- do_sym_list(list);
+
+ DO_LIST(filelist, file, do_sym_list(__sparse(file)));
}
diff --git a/dissect.h b/dissect.h
index 1f5b1d9e..a77a9328 100644
--- a/dissect.h
+++ b/dissect.h
@@ -4,6 +4,7 @@
#include <stdio.h>
#include "parse.h"
#include "expression.h"
+#include "scope.h"
#define U_SHIFT 8
@@ -25,6 +26,13 @@ struct reporter
void (*r_member)(unsigned, struct position *, struct symbol *, struct symbol *);
};
-extern void dissect(struct symbol_list *, struct reporter *);
+extern struct symbol *dissect_ctx;
+
+static inline bool sym_is_local(struct symbol *sym)
+{
+ return !toplevel(sym->scope);
+}
+
+extern void dissect(struct reporter *, struct string_list *);
#endif
diff --git a/evaluate.c b/evaluate.c
index f1a266be..b7bb1f52 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -3107,22 +3107,6 @@ static int evaluate_symbol_call(struct expression *expr)
if (ctype->op && ctype->op->evaluate)
return ctype->op->evaluate(expr);
- if (ctype->ctype.modifiers & MOD_INLINE) {
- int ret;
- struct symbol *curr = current_fn;
-
- if (ctype->definition)
- ctype = ctype->definition;
-
- current_fn = ctype->ctype.base_type;
-
- ret = inline_function(expr, ctype);
-
- /* restore the old function */
- current_fn = curr;
- return ret;
- }
-
return 0;
}
diff --git a/expand.c b/expand.c
index 36612c86..e7559878 100644
--- a/expand.c
+++ b/expand.c
@@ -910,6 +910,25 @@ static int expand_symbol_call(struct expression *expr, int cost)
if (fn->type != EXPR_PREOP)
return SIDE_EFFECTS;
+ if (ctype->ctype.modifiers & MOD_INLINE) {
+ struct symbol *def;
+
+ def = ctype->definition ? ctype->definition : ctype;
+ if (inline_function(expr, def)) {
+ struct symbol *fn = def->ctype.base_type;
+ struct symbol *curr = current_fn;
+
+ current_fn = fn;
+ evaluate_statement(expr->statement);
+ current_fn = curr;
+
+ fn->expanding = 1;
+ cost = expand_expression(expr);
+ fn->expanding = 0;
+ return cost;
+ }
+ }
+
if (ctype->op && ctype->op->expand)
return ctype->op->expand(expr, cost);
diff --git a/expression.c b/expression.c
index ffb6cb9d..5b9bddfe 100644
--- a/expression.c
+++ b/expression.c
@@ -873,7 +873,7 @@ struct token *conditional_expression(struct token *token, struct expression **tr
if (*tree && match_op(token, '?')) {
struct expression *expr = alloc_expression(token->pos, EXPR_CONDITIONAL);
expr->op = token->special;
- expr->left = *tree;
+ expr->conditional = *tree;
*tree = expr;
token = parse_expression(token->next, &expr->cond_true);
token = expect(token, ':', "in conditional expression");
diff --git a/inline.c b/inline.c
index 6f73a305..a9597280 100644
--- a/inline.c
+++ b/inline.c
@@ -519,8 +519,6 @@ int inline_function(struct expression *expr, struct symbol *sym)
if (fn->expanding)
return 0;
- fn->expanding = 1;
-
name_list = fn->arguments;
expr->type = EXPR_STATEMENT;
@@ -558,9 +556,6 @@ int inline_function(struct expression *expr, struct symbol *sym)
unset_replace_list(fn_symbol_list);
- evaluate_statement(stmt);
-
- fn->expanding = 0;
return 1;
}
diff --git a/lib.c b/lib.c
index f15e4d99..f9ec285e 100644
--- a/lib.c
+++ b/lib.c
@@ -280,6 +280,7 @@ int Woverride_init = 1;
int Woverride_init_all = 0;
int Woverride_init_whole_range = 0;
int Wparen_string = 0;
+int Wpedantic = 0;
int Wpointer_arith = 0;
int Wpointer_to_int_cast = 1;
int Wptr_subtraction_blows = 0;
@@ -725,6 +726,17 @@ static char **handle_switch_o(char *arg, char **next)
return next;
}
+static const struct flag pflags[] = {
+ { "pedantic", &Wpedantic, NULL, OPT_VAL, WARNING_ON },
+ { }
+};
+
+static char **handle_switch_p(char *arg, char **next)
+{
+ handle_switches(arg-1, arg, pflags);
+ return next;
+}
+
static const struct flag warnings[] = {
{ "address", &Waddress },
{ "address-space", &Waddress_space },
@@ -754,6 +766,7 @@ static const struct flag warnings[] = {
{ "override-init", &Woverride_init },
{ "override-init-all", &Woverride_init_all },
{ "paren-string", &Wparen_string },
+ { "pedantic", &Wpedantic },
{ "pointer-to-int-cast", &Wpointer_to_int_cast },
{ "ptr-subtraction-blows", &Wptr_subtraction_blows },
{ "return-void", &Wreturn_void },
@@ -1181,6 +1194,7 @@ static char **handle_switch(char *arg, char **next)
case 'n': return handle_switch_n(arg, next);
case 'o': return handle_switch_o(arg, next);
case 'O': return handle_switch_O(arg, next);
+ case 'p': return handle_switch_p(arg, next);
case 's': return handle_switch_s(arg, next);
case 'U': return handle_switch_U(arg, next);
case 'v': return handle_switch_v(arg, next);
diff --git a/lib.h b/lib.h
index 72651cef..b18295a8 100644
--- a/lib.h
+++ b/lib.h
@@ -169,6 +169,7 @@ extern int Woverride_init;
extern int Woverride_init_all;
extern int Woverride_init_whole_range;
extern int Wparen_string;
+extern int Wpedantic;
extern int Wpointer_arith;
extern int Wpointer_to_int_cast;
extern int Wptr_subtraction_blows;
diff --git a/linearize.c b/linearize.c
index 30ed2a30..b040d345 100644
--- a/linearize.c
+++ b/linearize.c
@@ -183,6 +183,7 @@ static const char *opcodes[] = {
[OP_BR] = "br",
[OP_CBR] = "cbr",
[OP_SWITCH] = "switch",
+ [OP_UNREACH] = "unreachable",
[OP_COMPUTEDGOTO] = "jmp *",
/* Binary */
@@ -399,6 +400,8 @@ const char *show_instruction(struct instruction *insn)
} END_FOR_EACH_PTR(jmp);
break;
}
+ case OP_UNREACH:
+ break;
case OP_PHISOURCE: {
struct instruction *phi;
@@ -654,6 +657,13 @@ static void add_one_insn(struct entrypoint *ep, struct instruction *insn)
}
}
+static void add_unreachable(struct entrypoint *ep)
+{
+ struct instruction *insn = alloc_instruction(OP_UNREACH, 0);
+ add_one_insn(ep, insn);
+ ep->active = NULL;
+}
+
static void set_activeblock(struct entrypoint *ep, struct basic_block *bb)
{
if (!bb_terminated(ep->active))
@@ -1499,6 +1509,11 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi
fn = expr->fn;
fntype = fn->ctype;
+
+ // handle builtins
+ if (fntype->op && fntype->op->linearize)
+ return fntype->op->linearize(ep, expr);
+
ctype = &fntype->ctype;
if (fntype->type == SYM_NODE)
fntype = fntype->ctype.base_type;
@@ -1548,6 +1563,9 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi
add_one_insn(ep, insn);
}
} END_FOR_EACH_PTR(context);
+
+ if (ctype->modifiers & MOD_NORETURN)
+ add_unreachable(ep);
}
return retval;
@@ -2513,3 +2531,37 @@ struct entrypoint *linearize_symbol(struct symbol *sym)
return linearize_fn(sym, base_type);
return NULL;
}
+
+/*
+ * Builtin functions
+ */
+
+static pseudo_t linearize_unreachable(struct entrypoint *ep, struct expression *exp)
+{
+ add_unreachable(ep);
+ return VOID;
+}
+
+static struct sym_init {
+ const char *name;
+ pseudo_t (*linearize)(struct entrypoint *, struct expression*);
+ struct symbol_op op;
+} builtins_table[] = {
+ // must be declared in builtin.c:declare_builtins[]
+ { "__builtin_unreachable", linearize_unreachable },
+ { }
+};
+
+void init_linearized_builtins(int stream)
+{
+ struct sym_init *ptr;
+
+ for (ptr = builtins_table; ptr->name; ptr++) {
+ struct symbol *sym;
+ sym = create_symbol(stream, ptr->name, SYM_NODE, NS_SYMBOL);
+ if (!sym->op)
+ sym->op = &ptr->op;
+ sym->op->type |= KW_BUILTIN;
+ ptr->op.linearize = ptr->linearize;
+ }
+}
diff --git a/opcode.def b/opcode.def
index 57d827f4..2583e2f4 100644
--- a/opcode.def
+++ b/opcode.def
@@ -10,6 +10,7 @@ OPCODE(RET, BADOP, BADOP, BADOP, 1, OPF_NONE)
OPCODE(BR, BADOP, BADOP, BADOP, 0, OPF_NONE)
OPCODE(CBR, BADOP, BADOP, BADOP, 1, OPF_NONE)
OPCODE(SWITCH, BADOP, BADOP, BADOP, 1, OPF_NONE)
+OPCODE(UNREACH, BADOP, BADOP, BADOP, 0, OPF_NONE)
OPCODE(COMPUTEDGOTO, BADOP, BADOP, BADOP, 1, OPF_NONE)
OPCODE_RANGE(TERMINATOR, RET, COMPUTEDGOTO)
diff --git a/parse.c b/parse.c
index fb05253b..a29c67c8 100644
--- a/parse.c
+++ b/parse.c
@@ -60,6 +60,7 @@ static declarator_t
thread_specifier, const_qualifier, volatile_qualifier;
static declarator_t restrict_qualifier;
static declarator_t atomic_qualifier;
+static declarator_t autotype_specifier;
static struct token *parse_if_statement(struct token *token, struct statement *stmt);
static struct token *parse_return_statement(struct token *token, struct statement *stmt);
@@ -213,6 +214,13 @@ static struct symbol_op typeof_op = {
.set = Set_S|Set_T,
};
+static struct symbol_op autotype_op = {
+ .type = KW_SPECIFIER,
+ .declarator = autotype_specifier,
+ .test = Set_Any,
+ .set = Set_S|Set_T,
+};
+
static struct symbol_op attribute_op = {
.type = KW_ATTRIBUTE,
.declarator = attribute_specifier,
@@ -505,6 +513,7 @@ static struct init_keyword {
{ "typeof", NS_TYPEDEF, .op = &typeof_op },
{ "__typeof", NS_TYPEDEF, .op = &typeof_op },
{ "__typeof__", NS_TYPEDEF, .op = &typeof_op },
+ { "__auto_type",NS_TYPEDEF, .op = &autotype_op },
{ "__attribute", NS_TYPEDEF, .op = &attribute_op },
{ "__attribute__", NS_TYPEDEF, .op = &attribute_op },
@@ -772,6 +781,7 @@ static struct token *struct_union_enum_specifier(enum type type,
}
sym = alloc_symbol(token->pos, type);
+ set_current_scope(sym); // used by dissect
token = parse(token->next, sym);
ctx->ctype.base_type = sym;
token = expect(token, '}', "at end of specifier");
@@ -1077,6 +1087,13 @@ static struct token *typeof_specifier(struct token *token, struct decl_state *ct
return expect(token, ')', "after typeof");
}
+static struct token *autotype_specifier(struct token *token, struct decl_state *ctx)
+{
+ ctx->ctype.base_type = &autotype_ctype;
+ ctx->autotype = 1;
+ return token;
+}
+
static struct token *ignore_attribute(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
struct expression *expr = NULL;
@@ -2984,6 +3001,11 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
}
}
} else if (base_type && base_type->type == SYM_FN) {
+ if (base_type->ctype.base_type == &autotype_ctype) {
+ sparse_error(decl->pos, "'%s()' has __auto_type return type",
+ show_ident(decl->ident));
+ base_type->ctype.base_type = &int_ctype;
+ }
if (base_type->ctype.base_type == &incomplete_ctype) {
warning(decl->pos, "'%s()' has implicit return type",
show_ident(decl->ident));
@@ -3036,6 +3058,23 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
}
}
+ if (ctx.autotype) {
+ const char *msg = NULL;
+ if (decl->ctype.base_type != &autotype_ctype)
+ msg = "on non-identifier";
+ else if (match_op(token, ','))
+ msg = "on declaration list";
+ else if (!decl->initializer)
+ msg = "without initializer";
+ else if (decl->initializer->type == EXPR_SYMBOL &&
+ decl->initializer->symbol == decl)
+ msg = "on self-init var";
+ if (msg) {
+ sparse_error(decl->pos, "__auto_type %s", msg);
+ decl->ctype.base_type = &bad_ctype;
+ }
+ }
+
if (!match_op(token, ','))
break;
diff --git a/pre-process.c b/pre-process.c
index 7b245a04..c8725dc8 100644
--- a/pre-process.c
+++ b/pre-process.c
@@ -48,6 +48,7 @@ static struct ident_list *macros; // only needed for -dD
static int false_nesting = 0;
static int counter_macro = 0; // __COUNTER__ expansion
static int include_level = 0;
+static int expanding = 0;
#define INCLUDEPATHS 300
const char *includepath[INCLUDEPATHS+1] = {
@@ -232,8 +233,13 @@ static int expand_one_symbol(struct token **list)
sym->expander(token);
return 1;
} else {
+ int rc;
+
sym->used_in = file_scope;
- return expand(list, sym);
+ expanding = 1;
+ rc = expand(list, sym);
+ expanding = 0;
+ return rc;
}
}
@@ -271,8 +277,6 @@ static struct token *collect_arg(struct token *prev, int vararg, struct position
while (!eof_token(next = scan_next(p))) {
if (next->pos.newline && match_op(next, '#')) {
if (!next->pos.noexpand) {
- sparse_error(next->pos,
- "directive in macro's argument list");
preprocessor_line(stream, p);
__free_token(next); /* Free the '#' token */
continue;
@@ -305,6 +309,7 @@ static struct token *collect_arg(struct token *prev, int vararg, struct position
next->pos.stream = pos->stream;
next->pos.line = pos->line;
next->pos.pos = pos->pos;
+ next->pos.newline = 0;
p = &next->next;
}
*p = &eof_token_entry;
@@ -755,6 +760,7 @@ static int expand(struct token **list, struct symbol *sym)
struct token *token = *list;
struct ident *expanding = token->ident;
struct token **tail;
+ struct token *expansion = sym->expansion;
int nargs = sym->arglist ? sym->arglist->count.normal : 0;
struct arg args[nargs];
@@ -774,7 +780,7 @@ static int expand(struct token **list, struct symbol *sym)
expanding->tainted = 1;
last = token->next;
- tail = substitute(list, sym->expansion, args);
+ tail = substitute(list, expansion, args);
/*
* Note that it won't be eof - at least TOKEN_UNTAINT will be there.
* We still can lose the newline flag if the sucker expands to nothing,
@@ -2073,6 +2079,7 @@ static void handle_preprocessor_line(struct stream *stream, struct token **line,
int (*handler)(struct stream *, struct token **, struct token *);
struct token *token = start->next;
int is_normal = 1;
+ int is_cond = 0; // is one of {is,ifdef,ifndef,elif,else,endif}
if (eof_token(token))
return;
@@ -2082,6 +2089,7 @@ static void handle_preprocessor_line(struct stream *stream, struct token **line,
if (sym) {
handler = sym->handler;
is_normal = sym->normal;
+ is_cond = !sym->normal;
} else {
handler = handle_nondirective;
}
@@ -2096,6 +2104,11 @@ static void handle_preprocessor_line(struct stream *stream, struct token **line,
if (false_nesting)
goto out;
}
+
+ if (expanding) {
+ if (!is_cond || Wpedantic)
+ warning(start->pos, "directive in macro's argument list");
+ }
if (!handler(stream, line, token)) /* all set */
return;
diff --git a/scope.c b/scope.c
index cbf2fcf5..420c0f5a 100644
--- a/scope.c
+++ b/scope.c
@@ -40,6 +40,11 @@ struct scope *block_scope = &builtin_scope, // regular automatic variables etc
*file_scope = &builtin_scope, // static
*global_scope = &builtin_scope; // externally visible
+void set_current_scope(struct symbol *sym)
+{
+ sym->scope = block_scope;
+}
+
void bind_scope(struct symbol *sym, struct scope *scope)
{
sym->scope = scope;
diff --git a/scope.h b/scope.h
index d9a14aa3..3cad514a 100644
--- a/scope.h
+++ b/scope.h
@@ -53,6 +53,7 @@ extern void end_symbol_scope(void);
extern void start_function_scope(void);
extern void end_function_scope(void);
+extern void set_current_scope(struct symbol *);
extern void bind_scope(struct symbol *, struct scope *);
extern void rebind_scope(struct symbol *, struct scope *);
diff --git a/sindex.1 b/sindex.1
new file mode 100644
index 00000000..e3e14ca3
--- /dev/null
+++ b/sindex.1
@@ -0,0 +1,151 @@
+.\" Sindex manpage by Alexey Gladkov
+.TH sindex "1"
+.
+.SH NAME
+sindex \- Semantic Indexer for C
+.
+.SH SYNOPSIS
+.B sindex
+[\fIoptions\fR]
+.br
+.B sindex
+[\fIoptions\fR] \fIadd\fR [\fIcommand options\fR] [\fI--\fR] [\fIcompiler options\fR] [\fIfiles...\fR]
+.br
+.B sindex
+[\fIoptions\fR] \fIrm\fR [\fIcommand options\fR] \fIpattern\fR
+.br
+.B sindex
+[\fIoptions\fR] \fIsearch\fR [\fIcommand options\fR] [\fIpattern\fR]
+.br
+.B sindex [\fIoptions\fR] \fIsearch\fR [\fIcommand options\fR] (\fI-e\fR|\fI-l\fR) \fIfilename\fR:\fIlinenr\fR:\fIcolumn\fR
+.br
+.SH DESCRIPTION
+.P
+sindex is the simple to use cscope-like tool based on sparse/dissect. Unlike
+cscope it runs after pre-processor and thus it can't index the code filtered out
+by ifdef's, but otoh it understands how the symbol is used and it can track the
+usage of struct members.
+.
+.SH SUBCOMMANDS
+.TP
+\fBadd\fR
+generates or updates semantic index file.
+.TP
+\fBrm\fR
+removes files from the index by \fIpattern\fR. The \fIpattern\fR is a
+.BR glob (7)
+wildcard pattern.
+.TP
+\fBsearch\fR
+queries information about symbol by \fIpattern\fR. The \fIpattern\fR is a
+.BR glob (7)
+wildcard pattern.
+.
+.SH COMMON OPTIONS
+.TP
+\fB-D\fR, \fB--database=FILE\fR
+specify database file (default: ./sindex.sqlite).
+.TP
+\fB-v\fR, \fB--verbose\fR
+show information about what is being done.
+.TP
+\fB-h\fR, \fB--help\fR
+show this text and exit.
+.
+.SH ADD OPTIONS
+.TP
+\fB--include-local-syms\fR
+include into the index local symbols.
+.
+.SH SEARCH OPTIONS
+.TP
+\fB-f\fR, \fB--format=STRING\fR
+specify an output format. Default: '(%m) %f\\t%l\\t%c\\t%C\\t%s' (see
+.BR FORMAT
+below).
+.TP
+\fB-p\fR, \fB--path=PATTERN\fR
+search symbols only in specified directories.
+.TP
+\fB-m\fR, \fB--mode=MODE\fR
+search only the specified type of access (see
+.BR MODE
+below).
+.TP
+\fB-k\fR, \fB--kind=KIND\fR
+specify a kind of symbol (see
+.BR KIND
+below).
+.TP
+\fB-e\fR, \fB--explain\fR
+Show what happens in the specified file position;
+.TP
+\fB-l\fR, \fB--location\fR
+Show usage of symbols from a specific file position;
+.TP
+\fB-v\fR, \fB--verbose\fR
+show information about what is being done;
+.TP
+\fB-h\fR, \fB--help\fR
+show this text and exit.
+.
+.SH FORMAT
+.TP
+\fB%m\fR
+access mode in human readable form (see
+.BR MODE
+below).
+.TP
+\fB%f\fR
+file name.
+.TP
+\fB%l\fR
+line number.
+.TP
+\fB%c\fR
+column number.
+.TP
+\fB%C\fR
+the name of the function in which the symbol occurs.
+.TP
+\fB%n\fR
+symbol name.
+.TP
+\fB%s\fR
+source code line. Indexer does not save source code lines. They are read from
+the file during the search.
+.
+.SH KIND
+.TP
+\fBf\fR
+function
+.TP
+\fBs\fR
+strict
+.TP
+\fBm\fR
+struct member
+.
+.SH MODE
+The \fBMODE\fR is dumped as a 3-letter string. The first letter denotes address
+of part, 2-nd - access by value, 3-rd - access by pointer. A special value
+\'\fIdef\fR\' means a symbol definition.
+.TP
+\fBr\fR
+read
+.TP
+\fBw\fR
+write
+.TP
+\fBm\fR
+read and write
+.
+.SH SEE ALSO
+.BR sparse (1)
+.
+.SH HOMEPAGE
+http://www.kernel.org/pub/software/devel/sparse/
+.
+.SH MAILING LIST
+linux-sparse@vger.kernel.org
+.
diff --git a/sindex.c b/sindex.c
new file mode 100644
index 00000000..ea092a4a
--- /dev/null
+++ b/sindex.c
@@ -0,0 +1,1164 @@
+/*
+ * sindex - semantic indexer for C.
+ *
+ * Copyright (C) 2020 Alexey Gladkov
+ */
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sqlite3.h>
+
+#include "dissect.h"
+
+#define U_DEF (0x100 << U_SHIFT)
+#define SINDEX_DATABASE_VERSION 1
+
+#define message(fmt, ...) sindex_error(0, 0, (fmt), ##__VA_ARGS__)
+
+static const char *progname;
+static const char *sindex_command = NULL;
+
+// common options
+static const char *sindex_dbfile = "sindex.sqlite";
+static int sindex_verbose = 0;
+static char cwd[PATH_MAX];
+static size_t n_cwd;
+
+// 'add' command options
+static struct string_list *sindex_filelist = NULL;
+static int sindex_include_local_syms = 0;
+
+struct sindex_streams {
+ sqlite3_int64 id;
+};
+
+static struct sindex_streams *sindex_streams = NULL;
+static int sindex_streams_nr = 0;
+
+// 'search' command options
+static int sindex_search_modmask;
+static int sindex_search_modmask_defined = 0;
+static int sindex_search_kind = 0;
+static char *sindex_search_path = NULL;
+static char *sindex_search_symbol = NULL;
+static const char *sindex_search_format = "(%m) %f\t%l\t%c\t%C\t%s";
+
+#define EXPLAIN_LOCATION 1
+#define USAGE_BY_LOCATION 2
+static int sindex_search_by_location;
+static char *sindex_search_filename;
+static int sindex_search_line;
+static int sindex_search_column;
+
+static sqlite3 *sindex_db = NULL;
+static sqlite3_stmt *lock_stmt = NULL;
+static sqlite3_stmt *unlock_stmt = NULL;
+static sqlite3_stmt *insert_rec_stmt = NULL;
+static sqlite3_stmt *select_file_stmt = NULL;
+static sqlite3_stmt *insert_file_stmt = NULL;
+static sqlite3_stmt *delete_file_stmt = NULL;
+
+struct command {
+ const char *name;
+ int dbflags;
+ void (*parse_cmdline)(int argc, char **argv);
+ void (*handler)(int argc, char **argv);
+};
+
+static void show_usage(void)
+{
+ if (sindex_command)
+ printf("Try '%s %s --help' for more information.\n",
+ progname, sindex_command);
+ else
+ printf("Try '%s --help' for more information.\n",
+ progname);
+ exit(1);
+}
+
+static void show_help(int ret)
+{
+ printf(
+ "Usage: %1$s [options]\n"
+ " or: %1$s [options] add [command options] [--] [compiler options] [files...]\n"
+ " or: %1$s [options] rm [command options] pattern\n"
+ " or: %1$s [options] search [command options] pattern\n"
+ "\n"
+ "These are common %1$s commands used in various situations:\n"
+ " add Generate or updates semantic index file for c-source code;\n"
+ " rm Remove files from the index by pattern;\n"
+ " search Make index queries.\n"
+ "\n"
+ "Options:\n"
+ " -D, --database=FILE Specify database file (default: %2$s);\n"
+ " -v, --verbose Show information about what is being done;\n"
+ " -h, --help Show this text and exit.\n"
+ "\n"
+ "Environment:\n"
+ " SINDEX_DATABASE Database file location.\n"
+ "\n"
+ "Report bugs to authors.\n"
+ "\n",
+ progname, sindex_dbfile);
+ exit(ret);
+}
+
+static void show_help_add(int ret)
+{
+ printf(
+ "Usage: %1$s add [options] [--] [compiler options] files...\n"
+ "\n"
+ "Utility creates or updates a symbol index.\n"
+ "\n"
+ "Options:\n"
+ " --include-local-syms Include into the index local symbols;\n"
+ " -v, --verbose Show information about what is being done;\n"
+ " -h, --help Show this text and exit.\n"
+ "\n"
+ "Environment:\n"
+ " SINDEX_BASEDIRE Project top directory.\n"
+ "\n"
+ "Report bugs to authors.\n"
+ "\n",
+ progname);
+ exit(ret);
+
+}
+
+static void show_help_rm(int ret)
+{
+ printf(
+ "Usage: %1$s rm [options] pattern\n"
+ "\n"
+ "Utility removes source files from the index.\n"
+ "The pattern is a glob(7) wildcard pattern.\n"
+ "\n"
+ "Options:\n"
+ " -v, --verbose Show information about what is being done;\n"
+ " -h, --help Show this text and exit.\n"
+ "\n"
+ "Report bugs to authors.\n"
+ "\n",
+ progname);
+ exit(ret);
+}
+
+static void show_help_search(int ret)
+{
+ printf(
+ "Usage: %1$s search [options] [pattern]\n"
+ " or: %1$s search [options] (-e|-l) filename[:linenr[:column]]\n"
+ "\n"
+ "Utility searches information about symbol by pattern.\n"
+ "The pattern is a glob(7) wildcard pattern.\n"
+ "\n"
+ "Options:\n"
+ " -f, --format=STRING Specify an output format;\n"
+ " -p, --path=PATTERN Search symbols only in specified directories;\n"
+ " -m, --mode=MODE Search only the specified type of access;\n"
+ " -k, --kind=KIND Specify a kind of symbol;\n"
+ " -e, --explain Show what happens in the specified file position;\n"
+ " -l, --location Show usage of symbols from a specific file position;\n"
+ " -v, --verbose Show information about what is being done;\n"
+ " -h, --help Show this text and exit.\n"
+ "\n"
+ "The KIND can be one of the following: `s', `f', `v', `m'.\n"
+ "\n"
+ "Report bugs to authors.\n"
+ "\n",
+ progname);
+ exit(ret);
+}
+
+static void sindex_print_progname(void)
+{
+ fprintf(stderr, "%s: ", progname);
+ if (sindex_command)
+ fprintf(stderr, "%s: ", sindex_command);
+}
+
+static void sindex_error(int status, int errnum, const char *fmt, ...)
+{
+ va_list ap;
+ sindex_print_progname();
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (errnum > 0)
+ fprintf(stderr, ": %s", strerror(errnum));
+
+ fprintf(stderr, "\n");
+
+ if (status)
+ exit(status);
+}
+
+static void set_search_modmask(const char *v)
+{
+ size_t n = strlen(v);
+
+ if (n != 1 && n != 3)
+ sindex_error(1, 0, "the length of mode value must be 1 or 3: %s", v);
+
+ sindex_search_modmask_defined = 1;
+ sindex_search_modmask = 0;
+
+ if (n == 1) {
+ switch (v[0]) {
+ case 'r': v = "rrr"; break;
+ case 'w': v = "ww-"; break;
+ case 'm': v = "mmm"; break;
+ case '-': v = "---"; break;
+ default: sindex_error(1, 0, "unknown modificator: %s", v);
+ }
+ } else if (!strcmp(v, "def")) {
+ sindex_search_modmask = U_DEF;
+ return;
+ }
+
+ static const int modes[] = {
+ U_R_AOF, U_W_AOF, U_R_AOF | U_W_AOF,
+ U_R_VAL, U_W_VAL, U_R_VAL | U_W_VAL,
+ U_R_PTR, U_W_PTR, U_R_PTR | U_W_PTR,
+ };
+
+ for (int i = 0; i < 3; i++) {
+ switch (v[i]) {
+ case 'r': sindex_search_modmask |= modes[i * 3]; break;
+ case 'w': sindex_search_modmask |= modes[i * 3 + 1]; break;
+ case 'm': sindex_search_modmask |= modes[i * 3 + 2]; break;
+ case '-': break;
+ default: sindex_error(1, 0,
+ "unknown modificator in the mode value"
+ " (`r', `w', `m' or `-' expected): %c", v[i]);
+ }
+ }
+}
+
+static void parse_cmdline(int argc, char **argv)
+{
+ static const struct option long_options[] = {
+ { "database", required_argument, NULL, 'D' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL }
+ };
+ int c;
+ char *env;
+
+ if ((env = getenv("SINDEX_DATABASE")) != NULL)
+ sindex_dbfile = env;
+
+ while ((c = getopt_long(argc, argv, "+D:vh", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'D':
+ sindex_dbfile = optarg;
+ break;
+ case 'v':
+ sindex_verbose++;
+ break;
+ case 'h':
+ show_help(0);
+ }
+ }
+
+ if (optind == argc) {
+ message("command required");
+ show_usage();
+ }
+}
+
+static void parse_cmdline_add(int argc, char **argv)
+{
+ static const struct option long_options[] = {
+ { "include-local-syms", no_argument, NULL, 1 },
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL }
+ };
+ int c;
+
+ opterr = 0;
+
+ while ((c = getopt_long(argc, argv, "+vh", long_options, NULL)) != -1) {
+ switch (c) {
+ case 1:
+ sindex_include_local_syms = 1;
+ break;
+ case 'v':
+ sindex_verbose++;
+ break;
+ case 'h':
+ show_help_add(0);
+ case '?':
+ goto done;
+ }
+ }
+done:
+ if (optind == argc) {
+ message("more arguments required");
+ show_usage();
+ }
+
+ // enforce tabstop
+ tabstop = 1;
+
+ // step back since sparse_initialize will ignore argv[0].
+ optind--;
+
+ sparse_initialize(argc - optind, argv + optind, &sindex_filelist);
+}
+
+static void parse_cmdline_rm(int argc, char **argv)
+{
+ static const struct option long_options[] = {
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL }
+ };
+ int c;
+
+ while ((c = getopt_long(argc, argv, "+vh", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'v':
+ sindex_verbose++;
+ break;
+ case 'h':
+ show_help_rm(0);
+ }
+ }
+
+ if (optind == argc) {
+ message("more arguments required");
+ show_usage();
+ }
+}
+
+static void parse_cmdline_search(int argc, char **argv)
+{
+ static const struct option long_options[] = {
+ { "explain", no_argument, NULL, 'e' },
+ { "format", required_argument, NULL, 'f' },
+ { "path", required_argument, NULL, 'p' },
+ { "location", no_argument, NULL, 'l' },
+ { "mode", required_argument, NULL, 'm' },
+ { "kind", required_argument, NULL, 'k' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL }
+ };
+ int c;
+
+ while ((c = getopt_long(argc, argv, "+ef:m:k:p:lvh", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'e':
+ sindex_search_by_location = EXPLAIN_LOCATION;
+ break;
+ case 'l':
+ sindex_search_by_location = USAGE_BY_LOCATION;
+ break;
+ case 'f':
+ sindex_search_format = optarg;
+ break;
+ case 'm':
+ set_search_modmask(optarg);
+ break;
+ case 'k':
+ sindex_search_kind = tolower(optarg[0]);
+ break;
+ case 'p':
+ sindex_search_path = optarg;
+ break;
+ case 'v':
+ sindex_verbose++;
+ break;
+ case 'h':
+ show_help_search(0);
+ }
+ }
+
+ if (sindex_search_by_location) {
+ char *str;
+
+ if (optind == argc)
+ sindex_error(1, 0, "one argument required");
+
+ str = argv[optind];
+
+ while (str) {
+ char *ptr;
+
+ if ((ptr = strchr(str, ':')) != NULL)
+ *ptr++ = '\0';
+
+ if (*str != '\0') {
+ if (!sindex_search_filename) {
+ sindex_search_filename = str;
+ } else if (!sindex_search_line) {
+ sindex_search_line = atoi(str);
+ } else if (!sindex_search_column) {
+ sindex_search_column = atoi(str);
+ }
+ }
+ str = ptr;
+ }
+ } else if (optind < argc)
+ sindex_search_symbol = argv[optind++];
+}
+
+static int query_appendf(sqlite3_str *query, const char *fmt, ...)
+{
+ int status;
+ va_list args;
+
+ va_start(args, fmt);
+ sqlite3_str_vappendf(query, fmt, args);
+ va_end(args);
+
+ if ((status = sqlite3_str_errcode(query)) == SQLITE_OK)
+ return 0;
+
+ if (status == SQLITE_NOMEM)
+ message("not enough memory");
+
+ if (status == SQLITE_TOOBIG)
+ message("string too big");
+
+ return -1;
+}
+
+static inline void sqlite_bind_text(sqlite3_stmt *stmt, const char *field, const char *var, int len)
+{
+ if (sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, field), var, len, SQLITE_STATIC) != SQLITE_OK)
+ sindex_error(1, 0, "unable to bind value for %s: %s", field, sqlite3_errmsg(sindex_db));
+}
+
+static inline void sqlite_bind_int64(sqlite3_stmt *stmt, const char *field, long long var)
+{
+ if (sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, field), var) != SQLITE_OK)
+ sindex_error(1, 0, "unable to bind value for %s: %s", field, sqlite3_errmsg(sindex_db));
+}
+
+static inline void sqlite_prepare(const char *sql, sqlite3_stmt **stmt)
+{
+ int ret;
+ do {
+ ret = sqlite3_prepare_v2(sindex_db, sql, -1, stmt, NULL);
+ if (ret != SQLITE_OK && ret != SQLITE_BUSY)
+ sindex_error(1, 0, "unable to prepare query: %s: %s", sqlite3_errmsg(sindex_db), sql);
+ } while (ret == SQLITE_BUSY);
+}
+
+static inline void sqlite_prepare_persistent(const char *sql, sqlite3_stmt **stmt)
+{
+ int ret;
+ do {
+ ret = sqlite3_prepare_v3(sindex_db, sql, -1, SQLITE_PREPARE_PERSISTENT, stmt, NULL);
+ if (ret != SQLITE_OK && ret != SQLITE_BUSY)
+ sindex_error(1, 0, "unable to prepare query: %s: %s", sqlite3_errmsg(sindex_db), sql);
+ } while (ret == SQLITE_BUSY);
+}
+
+static inline void sqlite_reset_stmt(sqlite3_stmt *stmt)
+{
+ // Contrary to the intuition of many, sqlite3_reset() does not reset the
+ // bindings on a prepared statement. Use this routine to reset all host
+ // parameters to NULL.
+ sqlite3_clear_bindings(stmt);
+ sqlite3_reset(stmt);
+}
+
+static int sqlite_run(sqlite3_stmt *stmt)
+{
+ int ret = sqlite3_step(stmt);
+ if (ret != SQLITE_DONE && ret != SQLITE_ROW)
+ sindex_error(1, 0, "unable to process query: %s: %s", sqlite3_errmsg(sindex_db), sqlite3_sql(stmt));
+ return ret;
+}
+
+static void sqlite_command(const char *sql)
+{
+ sqlite3_stmt *stmt;
+ sqlite_prepare(sql, &stmt);
+ sqlite_run(stmt);
+ sqlite3_finalize(stmt);
+}
+
+static sqlite3_int64 get_db_version(void)
+{
+ sqlite3_stmt *stmt;
+ sqlite3_int64 dbversion;
+
+ sqlite_prepare("PRAGMA user_version", &stmt);
+ sqlite_run(stmt);
+ dbversion = sqlite3_column_int64(stmt, 0);
+ sqlite3_finalize(stmt);
+
+ return dbversion;
+}
+
+static void set_db_version(void)
+{
+ char *sql;
+ sqlite3_str *query = sqlite3_str_new(sindex_db);
+
+ if (query_appendf(query, "PRAGMA user_version = %d", SINDEX_DATABASE_VERSION) < 0)
+ exit(1);
+
+ sql = sqlite3_str_finish(query);
+ sqlite_command(sql);
+ sqlite3_free(sql);
+}
+
+static void open_temp_database(void)
+{
+ static const char *database_schema[] = {
+ "ATTACH ':memory:' AS tempdb",
+ "CREATE TABLE tempdb.sindex ("
+ " file INTEGER NOT NULL,"
+ " line INTEGER NOT NULL,"
+ " column INTEGER NOT NULL,"
+ " symbol TEXT NOT NULL,"
+ " kind INTEGER NOT NULL,"
+ " context TEXT,"
+ " mode INTEGER NOT NULL"
+ ")",
+ NULL,
+ };
+
+ for (int i = 0; database_schema[i]; i++)
+ sqlite_command(database_schema[i]);
+}
+
+static void open_database(const char *filename, int flags)
+{
+ static const char *database_schema[] = {
+ "CREATE TABLE file ("
+ " id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " name TEXT UNIQUE NOT NULL,"
+ " mtime INTEGER NOT NULL"
+ ")",
+ "CREATE TABLE sindex ("
+ " file INTEGER NOT NULL REFERENCES file(id) ON DELETE CASCADE,"
+ " line INTEGER NOT NULL,"
+ " column INTEGER NOT NULL,"
+ " symbol TEXT NOT NULL,"
+ " kind INTEGER NOT NULL,"
+ " context TEXT,"
+ " mode INTEGER NOT NULL"
+ ")",
+ "CREATE UNIQUE INDEX sindex_0 ON sindex (symbol, kind, mode, file, line, column)",
+ "CREATE INDEX sindex_1 ON sindex (file)",
+ NULL,
+ };
+
+ int exists = !access(filename, R_OK);
+
+ if (sqlite3_open_v2(filename, &sindex_db, flags, NULL) != SQLITE_OK)
+ sindex_error(1, 0, "unable to open database: %s: %s", filename, sqlite3_errmsg(sindex_db));
+
+ sqlite_command("PRAGMA journal_mode = WAL");
+ sqlite_command("PRAGMA synchronous = OFF");
+ sqlite_command("PRAGMA secure_delete = FAST");
+ sqlite_command("PRAGMA busy_timeout = 2147483647");
+ sqlite_command("PRAGMA foreign_keys = ON");
+
+ if (exists) {
+ if (get_db_version() < SINDEX_DATABASE_VERSION)
+ sindex_error(1, 0, "%s: Database too old. Please rebuild it.", filename);
+ return;
+ }
+
+ set_db_version();
+
+ for (int i = 0; database_schema[i]; i++)
+ sqlite_command(database_schema[i]);
+}
+
+struct index_record {
+ const char *context;
+ int ctx_len;
+
+ const char *symbol;
+ int sym_len;
+
+ int kind;
+ unsigned int mode;
+ long long mtime;
+ sqlite3_int64 file;
+ int line;
+ int col;
+};
+
+static void insert_record(struct index_record *rec)
+{
+ sqlite_bind_text(insert_rec_stmt, "@context", rec->context, rec->ctx_len);
+ sqlite_bind_text(insert_rec_stmt, "@symbol", rec->symbol, rec->sym_len);
+ sqlite_bind_int64(insert_rec_stmt, "@kind", rec->kind);
+ sqlite_bind_int64(insert_rec_stmt, "@mode", rec->mode);
+ sqlite_bind_int64(insert_rec_stmt, "@file", rec->file);
+ sqlite_bind_int64(insert_rec_stmt, "@line", rec->line);
+ sqlite_bind_int64(insert_rec_stmt, "@column", rec->col);
+ sqlite_run(insert_rec_stmt);
+ sqlite_reset_stmt(insert_rec_stmt);
+}
+
+static void update_stream(void)
+{
+ if (sindex_streams_nr >= input_stream_nr)
+ return;
+
+ sindex_streams = realloc(sindex_streams, input_stream_nr * sizeof(struct sindex_streams));
+ if (!sindex_streams)
+ sindex_error(1, errno, "realloc");
+
+ sqlite_run(lock_stmt);
+
+ for (int i = sindex_streams_nr; i < input_stream_nr; i++) {
+ struct stat st;
+ const char *filename;
+ char fullname[PATH_MAX];
+ sqlite3_int64 cur_mtime = 0;
+
+ if (input_streams[i].fd != -1) {
+ /*
+ * FIXME: Files in the input_streams may be duplicated.
+ */
+ if (stat(input_streams[i].name, &st) < 0)
+ sindex_error(1, errno, "stat: %s", input_streams[i].name);
+
+ cur_mtime = st.st_mtime;
+
+ if (!realpath(input_streams[i].name, fullname))
+ sindex_error(1, errno, "realpath: %s", input_streams[i].name);
+
+ if (!strncmp(fullname, cwd, n_cwd) && fullname[n_cwd] == '/') {
+ filename = fullname + n_cwd + 1;
+ sindex_streams[i].id = 0;
+ } else {
+ sindex_streams[i].id = -1;
+ continue;
+ }
+ } else {
+ sindex_streams[i].id = -1;
+ continue;
+ }
+
+ if (sindex_verbose > 1)
+ message("filename: %s", filename);
+
+ sqlite_bind_text(select_file_stmt, "@name", filename, -1);
+
+ if (sqlite_run(select_file_stmt) == SQLITE_ROW) {
+ sqlite3_int64 old_mtime;
+
+ sindex_streams[i].id = sqlite3_column_int64(select_file_stmt, 0);
+ old_mtime = sqlite3_column_int64(select_file_stmt, 1);
+
+ sqlite_reset_stmt(select_file_stmt);
+
+ if (cur_mtime == old_mtime)
+ continue;
+
+ sqlite_bind_text(delete_file_stmt, "@name", filename, -1);
+ sqlite_run(delete_file_stmt);
+ sqlite_reset_stmt(delete_file_stmt);
+ }
+
+ sqlite_reset_stmt(select_file_stmt);
+
+ sqlite_bind_text(insert_file_stmt, "@name", filename, -1);
+ sqlite_bind_int64(insert_file_stmt, "@mtime", cur_mtime);
+ sqlite_run(insert_file_stmt);
+ sqlite_reset_stmt(insert_file_stmt);
+
+ sindex_streams[i].id = sqlite3_last_insert_rowid(sindex_db);
+ }
+
+ sqlite_run(unlock_stmt);
+
+ sindex_streams_nr = input_stream_nr;
+}
+
+static void r_symbol(unsigned mode, struct position *pos, struct symbol *sym)
+{
+ static struct ident null;
+ struct ident *ctx = &null;
+ struct index_record rec;
+
+ update_stream();
+
+ if (sindex_streams[pos->stream].id == -1)
+ return;
+
+ if (!sindex_include_local_syms && sym_is_local(sym))
+ return;
+
+ if (!sym->ident) {
+ warning(*pos, "empty ident");
+ return;
+ }
+
+ if (dissect_ctx)
+ ctx = dissect_ctx->ident;
+
+ rec.context = ctx->name;
+ rec.ctx_len = ctx->len;
+ rec.symbol = sym->ident->name;
+ rec.sym_len = sym->ident->len;
+ rec.kind = sym->kind;
+ rec.mode = mode;
+ rec.file = sindex_streams[pos->stream].id;
+ rec.line = pos->line;
+ rec.col = pos->pos;
+
+ insert_record(&rec);
+}
+
+static void r_member(unsigned mode, struct position *pos, struct symbol *sym, struct symbol *mem)
+{
+ static struct ident null;
+ static char memname[1024];
+ struct ident *ni, *si, *mi;
+ struct ident *ctx = &null;
+ struct index_record rec;
+
+ update_stream();
+
+ if (sindex_streams[pos->stream].id == -1)
+ return;
+
+ if (!sindex_include_local_syms && sym_is_local(sym))
+ return;
+
+ ni = built_in_ident("?");
+ si = sym->ident ?: ni;
+ /* mem == NULL means entire struct accessed */
+ mi = mem ? (mem->ident ?: ni) : built_in_ident("*");
+
+ if (dissect_ctx)
+ ctx = dissect_ctx->ident;
+
+ snprintf(memname, sizeof(memname), "%.*s.%.*s", si->len, si->name, mi->len, mi->name);
+
+ rec.context = ctx->name;
+ rec.ctx_len = ctx->len;
+ rec.symbol = memname;
+ rec.sym_len = si->len + mi->len + 1;
+ rec.kind = 'm';
+ rec.mode = mode;
+ rec.file = sindex_streams[pos->stream].id;
+ rec.line = pos->line;
+ rec.col = pos->pos;
+
+ insert_record(&rec);
+}
+
+static void r_symdef(struct symbol *sym)
+{
+ r_symbol(U_DEF, &sym->pos, sym);
+}
+
+static void r_memdef(struct symbol *sym, struct symbol *mem)
+{
+ r_member(U_DEF, &mem->pos, sym, mem);
+}
+
+static void command_add(int argc, char **argv)
+{
+ static struct reporter reporter = {
+ .r_symdef = r_symdef,
+ .r_symbol = r_symbol,
+ .r_memdef = r_memdef,
+ .r_member = r_member,
+ };
+
+ open_temp_database();
+
+ sqlite_prepare_persistent(
+ "BEGIN IMMEDIATE",
+ &lock_stmt);
+
+ sqlite_prepare_persistent(
+ "COMMIT",
+ &unlock_stmt);
+
+ sqlite_prepare_persistent(
+ "INSERT OR IGNORE INTO tempdb.sindex "
+ "(context, symbol, kind, mode, file, line, column) "
+ "VALUES (@context, @symbol, @kind, @mode, @file, @line, @column)",
+ &insert_rec_stmt);
+
+ sqlite_prepare_persistent(
+ "SELECT id, mtime FROM file WHERE name == @name",
+ &select_file_stmt);
+
+ sqlite_prepare_persistent(
+ "INSERT INTO file (name, mtime) VALUES (@name, @mtime)",
+ &insert_file_stmt);
+
+ sqlite_prepare_persistent(
+ "DELETE FROM file WHERE name == @name",
+ &delete_file_stmt);
+
+ dissect(&reporter, sindex_filelist);
+
+ sqlite_run(lock_stmt);
+ sqlite_command("INSERT OR IGNORE INTO sindex SELECT * FROM tempdb.sindex");
+ sqlite_run(unlock_stmt);
+
+ sqlite3_finalize(insert_rec_stmt);
+ sqlite3_finalize(select_file_stmt);
+ sqlite3_finalize(insert_file_stmt);
+ sqlite3_finalize(delete_file_stmt);
+ sqlite3_finalize(lock_stmt);
+ sqlite3_finalize(unlock_stmt);
+ free(sindex_streams);
+}
+
+static void command_rm(int argc, char **argv)
+{
+ sqlite3_stmt *stmt;
+
+ sqlite_command("BEGIN IMMEDIATE");
+ sqlite_prepare("DELETE FROM file WHERE name GLOB @file", &stmt);
+
+ if (sindex_verbose > 1)
+ message("SQL: %s", sqlite3_sql(stmt));
+
+ for (int i = 0; i < argc; i++) {
+ sqlite_bind_text(stmt, "@file", argv[i], -1);
+ sqlite_run(stmt);
+ sqlite_reset_stmt(stmt);
+ }
+
+ sqlite3_finalize(stmt);
+ sqlite_command("COMMIT");
+}
+
+static inline void print_mode(char *value)
+{
+ char str[3];
+ int v = atoi(value);
+
+ if (v == U_DEF) {
+ printf("def");
+ return;
+ }
+
+#define U(m) "-rwm"[(v / m) & 3]
+ str[0] = U(U_R_AOF);
+ str[1] = U(U_R_VAL);
+ str[2] = U(U_R_PTR);
+
+ printf("%.3s", str);
+#undef U
+}
+
+static char *sindex_file_name;
+static FILE *sindex_file_fd;
+static int sindex_file_lnum;
+static char *sindex_line;
+static size_t sindex_line_buflen;
+static int sindex_line_len;
+
+static void print_file_line(const char *name, int lnum)
+{
+ /*
+ * All files are sorted by name and line number. So, we can reopen
+ * the file and read it line by line.
+ */
+ if (!sindex_file_name || strcmp(sindex_file_name, name)) {
+ if (sindex_file_fd) {
+ fclose(sindex_file_fd);
+ free(sindex_file_name);
+ }
+
+ sindex_file_name = strdup(name);
+
+ if (!sindex_file_name)
+ sindex_error(1, errno, "strdup");
+
+ sindex_file_fd = fopen(name, "r");
+
+ if (!sindex_file_fd)
+ sindex_error(1, errno, "fopen: %s", name);
+
+ sindex_file_lnum = 0;
+ }
+
+ do {
+ if (sindex_file_lnum == lnum) {
+ if (sindex_line[sindex_line_len-1] == '\n')
+ sindex_line_len--;
+ printf("%.*s", sindex_line_len, sindex_line);
+ break;
+ }
+ sindex_file_lnum++;
+ errno = 0;
+ } while((sindex_line_len = getline(&sindex_line, &sindex_line_buflen, sindex_file_fd)) != -1);
+
+ if (errno && errno != EOF)
+ sindex_error(1, errno, "getline");
+}
+
+static int search_query_callback(void *data, int argc, char **argv, char **colname)
+{
+ char *fmt = (char *) sindex_search_format;
+ char buf[32];
+ int quote = 0;
+ int n = 0;
+
+ while (*fmt != '\0') {
+ char c = *fmt;
+
+ if (quote) {
+ quote = 0;
+ switch (c) {
+ case 't': c = '\t'; break;
+ case 'r': c = '\r'; break;
+ case 'n': c = '\n'; break;
+ }
+ } else if (c == '%') {
+ int colnum = 0;
+ char *pos = ++fmt;
+
+ c = *fmt;
+
+ if (c == '\0')
+ sindex_error(1, 0, "unexpected end of format string");
+
+ switch (c) {
+ case 'f': colnum = 0; goto print_string;
+ case 'l': colnum = 1; goto print_string;
+ case 'c': colnum = 2; goto print_string;
+ case 'C': colnum = 3; goto print_string;
+ case 'n': colnum = 4; goto print_string;
+ case 'm':
+ if (n) {
+ printf("%.*s", n, buf);
+ n = 0;
+ }
+ print_mode(argv[5]);
+ fmt++;
+ break;
+ case 'k':
+ if (n) {
+ printf("%.*s", n, buf);
+ n = 0;
+ }
+ printf("%c", atoi(argv[6]));
+ fmt++;
+ break;
+ case 's':
+ if (n) {
+ printf("%.*s", n, buf);
+ n = 0;
+ }
+ print_file_line(argv[0], atoi(argv[1]));
+ fmt++;
+ break;
+ case -1:
+print_string:
+ if (n) {
+ printf("%.*s", n, buf);
+ n = 0;
+ }
+ printf("%s", argv[colnum]);
+ fmt++;
+ break;
+ }
+
+ if (pos == fmt)
+ sindex_error(1, 0, "invalid format specification: %%%c", c);
+
+ continue;
+ } else if (c == '\\') {
+ quote = 1;
+ fmt++;
+ continue;
+ }
+
+ if (n == sizeof(buf)) {
+ printf("%.*s", n, buf);
+ n = 0;
+ }
+
+ buf[n++] = c;
+ fmt++;
+ }
+
+ if (n)
+ printf("%.*s", n, buf);
+ printf("\n");
+
+ return 0;
+}
+
+static void command_search(int argc, char **argv)
+{
+ char *sql;
+ char *dberr = NULL;
+ sqlite3_str *query = sqlite3_str_new(sindex_db);
+
+ if (query_appendf(query,
+ "SELECT"
+ " file.name,"
+ " sindex.line,"
+ " sindex.column,"
+ " sindex.context,"
+ " sindex.symbol,"
+ " sindex.mode,"
+ " sindex.kind "
+ "FROM sindex, file "
+ "WHERE sindex.file == file.id") < 0)
+ goto fail;
+
+ if (sindex_search_kind) {
+ if (query_appendf(query, " AND sindex.kind == %d", sindex_search_kind) < 0)
+ goto fail;
+ }
+
+ if (sindex_search_symbol) {
+ int ret;
+
+ if (query_appendf(query, " AND ") < 0)
+ goto fail;
+
+ if (strpbrk(sindex_search_symbol, "*?[]"))
+ ret = query_appendf(query, "sindex.symbol GLOB %Q", sindex_search_symbol);
+ else
+ ret = query_appendf(query, "sindex.symbol == %Q", sindex_search_symbol);
+
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (sindex_search_modmask_defined) {
+ if (!sindex_search_modmask) {
+ if (query_appendf(query, " AND sindex.mode == %d", sindex_search_modmask) < 0)
+ goto fail;
+ } else if (query_appendf(query, " AND (sindex.mode & %d) != 0", sindex_search_modmask) < 0)
+ goto fail;
+ }
+
+ if (sindex_search_path) {
+ if (query_appendf(query, " AND file.name GLOB %Q", sindex_search_path) < 0)
+ goto fail;
+ }
+
+ if (sindex_search_by_location == EXPLAIN_LOCATION) {
+ if (query_appendf(query, " AND file.name == %Q", sindex_search_filename) < 0)
+ goto fail;
+ if (sindex_search_line &&
+ query_appendf(query, " AND sindex.line == %d", sindex_search_line) < 0)
+ goto fail;
+ if (sindex_search_column &&
+ query_appendf(query, " AND sindex.column == %d", sindex_search_column) < 0)
+ goto fail;
+ } else if (sindex_search_by_location == USAGE_BY_LOCATION) {
+ if (query_appendf(query, " AND sindex.symbol IN (") < 0)
+ goto fail;
+ if (query_appendf(query,
+ "SELECT sindex.symbol FROM sindex, file WHERE"
+ " sindex.file == file.id AND"
+ " file.name == %Q", sindex_search_filename) < 0)
+ goto fail;
+ if (sindex_search_line &&
+ query_appendf(query, " AND sindex.line == %d", sindex_search_line) < 0)
+ goto fail;
+ if (sindex_search_column &&
+ query_appendf(query, " AND sindex.column == %d", sindex_search_column) < 0)
+ goto fail;
+ if (query_appendf(query, ")") < 0)
+ goto fail;
+ }
+
+ if (query_appendf(query, " ORDER BY file.name, sindex.line, sindex.column ASC", sindex_search_path) < 0)
+ goto fail;
+
+ sql = sqlite3_str_value(query);
+
+ if (sindex_verbose > 1)
+ message("SQL: %s", sql);
+
+ sqlite3_exec(sindex_db, sql, search_query_callback, NULL, &dberr);
+ if (dberr)
+ sindex_error(1, 0, "sql query failed: %s", dberr);
+fail:
+ sql = sqlite3_str_finish(query);
+ sqlite3_free(sql);
+
+ if (sindex_file_fd) {
+ fclose(sindex_file_fd);
+ free(sindex_file_name);
+ }
+ free(sindex_line);
+}
+
+
+int main(int argc, char **argv)
+{
+ static const struct command commands[] = {
+ {
+ .name = "add",
+ .dbflags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+ .parse_cmdline = parse_cmdline_add,
+ .handler = command_add
+ },
+ {
+ .name = "rm",
+ .dbflags = SQLITE_OPEN_READWRITE,
+ .parse_cmdline = parse_cmdline_rm,
+ .handler = command_rm
+ },
+ {
+ .name = "search",
+ .dbflags = SQLITE_OPEN_READONLY,
+ .parse_cmdline = parse_cmdline_search,
+ .handler = command_search
+ },
+ { .name = NULL },
+ };
+ const struct command *cmd;
+
+ if (!(progname = rindex(argv[0], '/')))
+ progname = argv[0];
+ else
+ progname++;
+
+ if (!realpath(".", cwd))
+ sindex_error(1, errno, "unable to get current directory");
+ n_cwd = strlen(cwd);
+
+ parse_cmdline(argc, argv);
+
+ for (cmd = commands; cmd->name && strcmp(argv[optind], cmd->name); cmd++);
+ if (!cmd->name)
+ sindex_error(1, 0, "unknown command: %s", argv[optind]);
+ optind++;
+
+ sindex_command = cmd->name;
+
+ if (cmd->parse_cmdline)
+ cmd->parse_cmdline(argc, argv);
+
+ open_database(sindex_dbfile, cmd->dbflags);
+ cmd->handler(argc - optind, argv + optind);
+
+ sqlite3_close(sindex_db);
+
+ return 0;
+}
diff --git a/symbol.c b/symbol.c
index ab6e9841..c2e6f0b4 100644
--- a/symbol.c
+++ b/symbol.c
@@ -192,6 +192,10 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance
fn = advance ? lay_out_struct : lay_out_union;
FOR_EACH_PTR(sym->symbol_list, member) {
+ if (member->ctype.base_type == &autotype_ctype) {
+ sparse_error(member->pos, "member '%s' has __auto_type", show_ident(member->ident));
+ member->ctype.base_type = &incomplete_ctype;
+ }
fn(member, &info);
} END_FOR_EACH_PTR(member);
@@ -210,6 +214,19 @@ static struct symbol *examine_base_type(struct symbol *sym)
{
struct symbol *base_type;
+ if (sym->ctype.base_type == &autotype_ctype) {
+ struct symbol *type = evaluate_expression(sym->initializer);
+ if (!type)
+ type = &bad_ctype;
+ if (is_bitfield_type(type)) {
+ warning(sym->pos, "__auto_type on bitfield");
+ if (type->type == SYM_NODE)
+ type = type->ctype.base_type;
+ type = type->ctype.base_type;
+ }
+ sym->ctype.base_type = type;
+ }
+
/* Check the base type */
base_type = examine_symbol_type(sym->ctype.base_type);
if (!base_type || base_type->type == SYM_PTR)
@@ -734,6 +751,7 @@ struct symbol bool_ctype, void_ctype, type_ctype,
string_ctype, ptr_ctype, lazy_ptr_ctype,
incomplete_ctype, label_ctype, bad_ctype,
null_ctype;
+struct symbol autotype_ctype;
struct symbol int_ptr_ctype, uint_ptr_ctype;
struct symbol long_ptr_ctype, ulong_ptr_ctype;
struct symbol llong_ptr_ctype, ullong_ptr_ctype;
@@ -792,6 +810,7 @@ static const struct ctype_declare {
{ &void_ctype, T_BASETYPE },
{ &type_ctype, T_BASETYPE },
{ &incomplete_ctype, T_BASETYPE },
+ { &autotype_ctype, T_BASETYPE },
{ &bad_ctype, T_BASETYPE },
{ &char_ctype, T__INT(-2, char) },
diff --git a/symbol.h b/symbol.h
index 2b8aa2d8..18476582 100644
--- a/symbol.h
+++ b/symbol.h
@@ -78,7 +78,7 @@ enum keyword {
KW_MODIFIER = 1 << 1,
KW_QUALIFIER = 1 << 2,
KW_ATTRIBUTE = 1 << 3,
- // KW UNUSED = 1 << 4,
+ KW_BUILTIN = 1 << 4,
KW_ASM = 1 << 5,
KW_MODE = 1 << 6,
// KW UNUSED = 1 << 7,
@@ -110,13 +110,18 @@ struct decl_state {
unsigned long f_modifiers; // function attributes
unsigned char prefer_abstract, is_inline, storage_class, is_tls;
unsigned char is_ext_visible;
+ unsigned char autotype;
};
+struct pseudo;
+struct entrypoint;
+
struct symbol_op {
enum keyword type;
int (*evaluate)(struct expression *);
int (*expand)(struct expression *, int);
int (*args)(struct expression *);
+ struct pseudo *(*linearize)(struct entrypoint *, struct expression *);
/* keywords */
struct token *(*declarator)(struct token *token, struct decl_state *ctx);
@@ -277,6 +282,7 @@ extern struct symbol bool_ctype, void_ctype, type_ctype,
string_ctype, ptr_ctype, lazy_ptr_ctype,
incomplete_ctype, label_ctype, bad_ctype,
null_ctype;
+extern struct symbol autotype_ctype;
extern struct symbol int_ptr_ctype, uint_ptr_ctype;
extern struct symbol long_ptr_ctype, ulong_ptr_ctype;
extern struct symbol llong_ptr_ctype, ullong_ptr_ctype;
@@ -308,6 +314,7 @@ extern struct symbol *lookup_symbol(struct ident *, enum namespace);
extern struct symbol *create_symbol(int stream, const char *name, int type, int namespace);
extern void init_symbols(void);
extern void init_builtins(int stream);
+extern void init_linearized_builtins(int stream);
extern void declare_builtins(void);
extern void init_ctype(void);
extern struct symbol *alloc_symbol(struct position, int type);
diff --git a/test-dissect.c b/test-dissect.c
index e725eec5..58b3e633 100644
--- a/test-dissect.c
+++ b/test-dissect.c
@@ -1,18 +1,5 @@
#include "dissect.h"
-static unsigned dotc_stream;
-
-static inline char storage(struct symbol *sym)
-{
- int t = sym->type;
- unsigned m = sym->ctype.modifiers;
-
- if (m & MOD_INLINE || t == SYM_STRUCT || t == SYM_UNION /*|| t == SYM_ENUM*/)
- return sym->pos.stream == dotc_stream ? 's' : 'g';
-
- return (m & MOD_STATIC) ? 's' : (m & MOD_NONLOCAL) ? 'g' : 'l';
-}
-
static inline const char *show_mode(unsigned mode)
{
static char str[3];
@@ -32,14 +19,30 @@ static inline const char *show_mode(unsigned mode)
static void print_usage(struct position *pos, struct symbol *sym, unsigned mode)
{
static unsigned curr_stream = -1;
+ static struct ident null;
+ struct ident *ctx = &null;
if (curr_stream != pos->stream) {
curr_stream = pos->stream;
printf("\nFILE: %s\n\n", stream_name(curr_stream));
}
- printf("%4d:%-3d %c %-5.3s",
- pos->line, pos->pos, storage(sym), show_mode(mode));
+ if (dissect_ctx)
+ ctx = dissect_ctx->ident;
+
+ printf("%4d:%-3d %-16.*s %s ",
+ pos->line, pos->pos, ctx->len, ctx->name, show_mode(mode));
+
+}
+
+static char symscope(struct symbol *sym)
+{
+ if (sym_is_local(sym)) {
+ if (!dissect_ctx)
+ warning(sym->pos, "no context");
+ return '.';
+ }
+ return ' ';
}
static void r_symbol(unsigned mode, struct position *pos, struct symbol *sym)
@@ -49,25 +52,52 @@ static void r_symbol(unsigned mode, struct position *pos, struct symbol *sym)
if (!sym->ident)
sym->ident = built_in_ident("__asm__");
- printf("%-32.*s %s\n",
- sym->ident->len, sym->ident->name,
+ printf("%c %c %-32.*s %s\n",
+ symscope(sym), sym->kind, sym->ident->len, sym->ident->name,
show_typename(sym->ctype.base_type));
+
+ switch (sym->kind) {
+ case 's':
+ if (sym->type == SYM_STRUCT || sym->type == SYM_UNION)
+ break;
+ goto err;
+
+ case 'f':
+ if (sym->type != SYM_BAD && sym->ctype.base_type->type != SYM_FN)
+ goto err;
+ case 'v':
+ if (sym->type == SYM_NODE || sym->type == SYM_BAD)
+ break;
+ goto err;
+ default:
+ goto err;
+ }
+
+ return;
+err:
+ warning(*pos, "r_symbol bad sym type=%d kind=%d", sym->type, sym->kind);
}
static void r_member(unsigned mode, struct position *pos, struct symbol *sym, struct symbol *mem)
{
- struct ident *si, *mi;
+ struct ident *ni, *si, *mi;
print_usage(pos, sym, mode);
- si = sym->ident ?: built_in_ident("?");
+ ni = built_in_ident("?");
+ si = sym->ident ?: ni;
/* mem == NULL means entire struct accessed */
- mi = mem ? mem->ident : built_in_ident("*");
+ mi = mem ? (mem->ident ?: ni) : built_in_ident("*");
- printf("%.*s.%-*.*s %s\n",
- si->len, si->name,
+ printf("%c m %.*s.%-*.*s %s\n",
+ symscope(sym), si->len, si->name,
32-1 - si->len, mi->len, mi->name,
show_typename(mem ? mem->ctype.base_type : sym));
+
+ if (sym->ident && sym->kind != 's')
+ warning(*pos, "r_member bad sym type=%d kind=%d", sym->type, sym->kind);
+ if (mem && mem->kind != 'm')
+ warning(*pos, "r_member bad mem->kind = %d", mem->kind);
}
static void r_symdef(struct symbol *sym)
@@ -88,15 +118,10 @@ int main(int argc, char **argv)
.r_symbol = r_symbol,
.r_member = r_member,
};
- struct string_list *filelist = NULL;
- char *file;
+ struct string_list *filelist = NULL;
sparse_initialize(argc, argv, &filelist);
-
- FOR_EACH_PTR(filelist, file) {
- dotc_stream = input_stream_nr;
- dissect(__sparse(file), &reporter);
- } END_FOR_EACH_PTR(file);
+ dissect(&reporter, filelist);
return 0;
}
diff --git a/validation/autotype-ko.c b/validation/autotype-ko.c
new file mode 100644
index 00000000..5b6cd708
--- /dev/null
+++ b/validation/autotype-ko.c
@@ -0,0 +1,45 @@
+__auto_type u; // KO: no initializer
+__auto_type r[2] = { 0, 1 }; // KO: not a plain identifier
+__auto_type foo(void) { } // KO: not a plain identifier
+__auto_type v = 0, w = 1; // KO: in list
+struct { __auto_type x; } s; // KO: not valid for struct/union
+__auto_type self = self; // KO: self-declared
+__auto_type undc = this; // KO: undeclared
+
+int i = 1;
+double f = 1.0;
+__auto_type i = 2; // KO: redecl, same type
+__auto_type f = 2.0f; // KO: redecl, diff type
+
+
+static int foo(int a, const int *ptr)
+{
+ __auto_type i = a;
+ __auto_type c = *ptr;
+
+ c += 1;
+ return i;
+}
+
+/*
+ * check-name: autotype-ko
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+autotype-ko.c:1:13: error: __auto_type without initializer
+autotype-ko.c:2:13: error: __auto_type on non-identifier
+autotype-ko.c:3:13: error: 'foo()' has __auto_type return type
+autotype-ko.c:4:13: error: __auto_type on declaration list
+autotype-ko.c:6:13: error: __auto_type on self-init var
+autotype-ko.c:2:20: error: invalid initializer
+autotype-ko.c:5:22: error: member 'x' has __auto_type
+autotype-ko.c:7:20: error: undefined identifier 'this'
+autotype-ko.c:11:13: error: symbol 'i' has multiple initializers (originally initialized at autotype-ko.c:9)
+autotype-ko.c:12:13: error: symbol 'f' has multiple initializers (originally initialized at autotype-ko.c:10)
+autotype-ko.c:12:13: error: symbol 'f' redeclared with different type (different type sizes):
+autotype-ko.c:12:13: float [addressable] [toplevel] f
+autotype-ko.c:10:8: note: previously declared as:
+autotype-ko.c:10:8: double [addressable] [toplevel] f
+autotype-ko.c:20:9: error: assignment to const expression
+ * check-error-end
+ */
diff --git a/validation/autotype.c b/validation/autotype.c
new file mode 100644
index 00000000..98683c93
--- /dev/null
+++ b/validation/autotype.c
@@ -0,0 +1,55 @@
+#ifdef __CHECKER__
+#define is_type(X, T) _Static_assert([typeof(X)] == [T], "")
+#else
+#define is_type(X, T) _Static_assert(1, "")
+#endif
+
+struct s {
+ int x;
+ int bf:3;
+};
+
+extern char ch;
+extern const int ci;
+
+__auto_type i = 0; is_type(i, int);
+__auto_type m = 1UL; is_type(m, unsigned long);
+__auto_type l = (int)0L; is_type(l, int);
+__auto_type c = (char)'\n'; is_type(c, char);
+__auto_type p = &i; is_type(p, int *);
+__auto_type f = 0.0; is_type(f, double);
+__auto_type s = (struct s){0}; is_type(s, struct s);
+__auto_type pci = &ci; is_type(pci, const int *);
+
+// ~~: not valid for bitfield
+__auto_type b = (struct s){0}.bf; is_type(b, int);
+
+static __auto_type si = 0; is_type(si, int);
+const __auto_type ci = 0; is_type(ci, const int);
+__auto_type ch = (char) '\n'; is_type(ch, char);
+
+static int foo(int a)
+{
+ __auto_type i = a; is_type(i, int);
+ __auto_type c = ch; is_type(c, char);
+ __auto_type ct = ci; is_type(&ct, const int *);
+
+ return ct += i + c;
+}
+
+
+
+#define __as __attribute__((address_space(42)))
+extern int __as aa;
+
+__auto_type pa = &aa; is_type(pa, int __as *);
+
+/*
+ * check-name: autotype
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+autotype.c:25:13: warning: __auto_type on bitfield
+autotype.c:37:16: error: assignment to const expression
+ * check-error-end
+ */
diff --git a/validation/context-unreachable.c b/validation/context-unreachable.c
new file mode 100644
index 00000000..8664962e
--- /dev/null
+++ b/validation/context-unreachable.c
@@ -0,0 +1,15 @@
+int fun(void);
+
+static void foo(void)
+{
+ __context__(1);
+ if (!fun()) {
+ __builtin_unreachable();
+ return;
+ }
+ __context__(-1);
+}
+
+/*
+ * check-name: context-unreachable
+ */
diff --git a/validation/expand/builtin_constant_inline0.c b/validation/expand/builtin_constant_inline0.c
new file mode 100644
index 00000000..a0057f20
--- /dev/null
+++ b/validation/expand/builtin_constant_inline0.c
@@ -0,0 +1,23 @@
+static inline int is_const(long size)
+{
+ return __builtin_constant_p(size) ? size : 0;
+}
+
+int foo(void)
+{
+ return is_const(42);
+}
+
+/*
+ * check-name: builtin_constant_inline0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+foo:
+.L0:
+ <entry-point>
+ ret.32 $42
+
+
+ * check-output-end
+ */
diff --git a/validation/expand/builtin_constant_inline1.c b/validation/expand/builtin_constant_inline1.c
new file mode 100644
index 00000000..da08f960
--- /dev/null
+++ b/validation/expand/builtin_constant_inline1.c
@@ -0,0 +1,24 @@
+static inline void fun(void) { }
+
+#define EXPR ({ fun(); 42; })
+
+int bar(void)
+{
+ // GCC doesn't consider EXPR as a constant
+ return __builtin_constant_p(EXPR);
+}
+
+/*
+ * check-name: builtin_constant_inline1
+ * check-command: test-linearize -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-start
+bar:
+.L0:
+ <entry-point>
+ ret.32 $0
+
+
+ * check-output-end
+ */
diff --git a/validation/inline_base0.c b/validation/inline_base0.c
new file mode 100644
index 00000000..517ee972
--- /dev/null
+++ b/validation/inline_base0.c
@@ -0,0 +1,47 @@
+static inline int add(int a, int b)
+{
+ return a + b;
+}
+
+int foo0(int x, int y)
+{
+ return add(x, y);
+}
+
+int foo1(int x)
+{
+ return add(x, 1);
+}
+
+int foo2(void)
+{
+ return add(1, 2);
+}
+
+/*
+ * check-name: inline_base0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+foo0:
+.L0:
+ <entry-point>
+ add.32 %r5 <- %arg1, %arg2
+ ret.32 %r5
+
+
+foo1:
+.L3:
+ <entry-point>
+ add.32 %r10 <- %arg1, $1
+ ret.32 %r10
+
+
+foo2:
+.L6:
+ <entry-point>
+ ret.32 $3
+
+
+ * check-output-end
+ */
diff --git a/validation/inline_self.c b/validation/inline_self.c
new file mode 100644
index 00000000..14c2b0b2
--- /dev/null
+++ b/validation/inline_self.c
@@ -0,0 +1,13 @@
+static inline void foo(void)
+{
+ foo();
+}
+
+static void baz(void)
+{
+ foo();
+}
+
+/*
+ * check-name: inline_self
+ */
diff --git a/validation/linear/builtin_unreachable0.c b/validation/linear/builtin_unreachable0.c
new file mode 100644
index 00000000..911ed7f9
--- /dev/null
+++ b/validation/linear/builtin_unreachable0.c
@@ -0,0 +1,27 @@
+int foo(int p)
+{
+ if (p == 3)
+ __builtin_unreachable();
+ return p;
+}
+
+/*
+ * check-name: builtin_unreachable0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+foo:
+.L0:
+ <entry-point>
+ seteq.32 %r2 <- %arg1, $3
+ cbr %r2, .L1, .L3
+
+.L1:
+ unreachable
+
+.L3:
+ ret.32 %arg1
+
+
+ * check-output-end
+ */
diff --git a/validation/linear/builtin_unreachable.c b/validation/linear/builtin_unreachable1.c
index 4f13b892..70f6674c 100644
--- a/validation/linear/builtin_unreachable.c
+++ b/validation/linear/builtin_unreachable1.c
@@ -1,31 +1,30 @@
-void function_that_never_returns(void);
+void die(void);
int foo(int c)
{
if (c)
return 1;
- function_that_never_returns();
+ die();
__builtin_unreachable();
}
/*
- * check-name: __builtin_unreachable()
+ * check-name: builtin_unreachable1
* check-command: test-linearize -Wno-decl $file
*
- * check-known-to-fail
* check-output-start
foo:
.L0:
<entry-point>
cbr %arg1, .L3, .L2
-.L2:
- call function_that_never_returns
- unreach
-
.L3:
ret.32 $1
+.L2:
+ call die
+ unreachable
+
* check-output-end
*/
diff --git a/validation/linear/noreturn-unreachable0.c b/validation/linear/noreturn-unreachable0.c
new file mode 100644
index 00000000..9bcd605f
--- /dev/null
+++ b/validation/linear/noreturn-unreachable0.c
@@ -0,0 +1,22 @@
+extern void die(void) __attribute__((noreturn));
+
+int foo(void)
+{
+ die();
+ return 0;
+}
+
+/*
+ * check-name: noreturn-unreachable0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+foo:
+.L0:
+ <entry-point>
+ call die
+ unreachable
+
+
+ * check-output-end
+ */
diff --git a/validation/preprocessor/directive-within-macro.c b/validation/preprocessor/directive-within-macro.c
new file mode 100644
index 00000000..4562078a
--- /dev/null
+++ b/validation/preprocessor/directive-within-macro.c
@@ -0,0 +1,36 @@
+#define f(x) x
+
+f(1
+#if 1 // OK
+ a
+#elif 2 // OK
+ b
+#else // OK
+ c
+#endif // OK
+#ifdef f // OK
+ d
+#endif // OK
+#ifndef f // OK
+ e
+#endif // OK
+ 3)
+
+f(1
+#define x y // KO
+ 3)
+
+/*
+ * check-name: directive-within-macro
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+1 a d 3
+1 3
+ * check-output-end
+ *
+ * check-error-start
+preprocessor/directive-within-macro.c:20:1: warning: directive in macro's argument list
+ * check-error-end
+ */
diff --git a/validation/preprocessor/expand-and-nl.c b/validation/preprocessor/expand-and-nl.c
new file mode 100644
index 00000000..1a5f2265
--- /dev/null
+++ b/validation/preprocessor/expand-and-nl.c
@@ -0,0 +1,13 @@
+#define M(X) X-X
+
+M(a
+b)
+/*
+ * check-name: expand-and-nl
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+a b-a b
+ * check-output-end
+ */
diff --git a/validation/preprocessor/expand-redef.c b/validation/preprocessor/expand-redef.c
new file mode 100644
index 00000000..0ccf0d4c
--- /dev/null
+++ b/validation/preprocessor/expand-redef.c
@@ -0,0 +1,20 @@
+#define f(x) x x
+f(1
+#undef f
+#define f 2
+ f)
+
+/*
+ * check-name: expand-redef
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+1 2 1 2
+ * check-output-end
+ *
+ * check-error-start
+preprocessor/expand-redef.c:3:1: warning: directive in macro's argument list
+preprocessor/expand-redef.c:4:1: warning: directive in macro's argument list
+ * check-error-end
+ */
diff --git a/validation/preprocessor/preprocessor22.c b/validation/preprocessor/preprocessor22.c
index fb28daaa..0d13c5f8 100644
--- a/validation/preprocessor/preprocessor22.c
+++ b/validation/preprocessor/preprocessor22.c
@@ -17,19 +17,17 @@ define_struct(a, {
* check-description: Directives are not allowed within a macro argument list,
* although cpp deals with it to treat macro more like C functions.
*
- * check-command: sparse -E $file
+ * check-command: sparse -pedantic -E $file
*
* check-error-start
-preprocessor/preprocessor22.c:6:1: error: directive in macro's argument list
-preprocessor/preprocessor22.c:8:1: error: directive in macro's argument list
-preprocessor/preprocessor22.c:10:1: error: directive in macro's argument list
-preprocessor/preprocessor22.c:12:1: error: directive in macro's argument list
+preprocessor/preprocessor22.c:6:1: warning: directive in macro's argument list
+preprocessor/preprocessor22.c:8:1: warning: directive in macro's argument list
+preprocessor/preprocessor22.c:10:1: warning: directive in macro's argument list
+preprocessor/preprocessor22.c:12:1: warning: directive in macro's argument list
* check-error-end
*
* check-output-start
-struct {
-int b;
-} a;;
+struct { int b; } a;;
* check-output-end
*/