diff options
-rw-r--r-- | Documentation/annotations.rst | 85 | ||||
-rw-r--r-- | Documentation/index.rst | 28 | ||||
-rw-r--r-- | Documentation/nocast-vs-bitwise.md | 43 | ||||
-rw-r--r-- | Documentation/submitting-patches.md | 4 | ||||
-rw-r--r-- | Documentation/templates/breadcrumbs.html | 11 | ||||
-rw-r--r-- | Documentation/templates/layout.html | 8 | ||||
-rw-r--r-- | char.c | 5 | ||||
-rw-r--r-- | evaluate.c | 27 | ||||
-rw-r--r-- | parse.c | 336 | ||||
-rw-r--r-- | show-parse.c | 20 | ||||
-rw-r--r-- | symbol.c | 25 | ||||
-rw-r--r-- | symbol.h | 16 | ||||
-rw-r--r-- | tokenize.c | 7 | ||||
-rw-r--r-- | validation/empty-char-constant.c | 9 | ||||
-rw-r--r-- | validation/init-wstring.c | 40 | ||||
-rw-r--r-- | validation/init_cstring.c | 2 | ||||
-rw-r--r-- | validation/linear/bug-assign-op0.c | 10 | ||||
-rw-r--r-- | validation/preprocessor/empty-char-constant.c | 13 |
18 files changed, 379 insertions, 310 deletions
diff --git a/Documentation/annotations.rst b/Documentation/annotations.rst new file mode 100644 index 00000000..d4b5546f --- /dev/null +++ b/Documentation/annotations.rst @@ -0,0 +1,85 @@ +Annotations +=========== + +Sparse extends C's type system with a number of extra type qualifiers +which add restrictions on what you can do on objects annotated with them. +These qualifiers are specified with GCC's ``__attribute__`` syntax. + +address_space(*name*) +--------------------- +This attribute is to be used on pointers to specify that its target is +in address space *name* (an identifier or a constant integer). + +Sparse treats pointers with different address spaces as distinct types +and will warn on casts (implicit or explicit) mixing the address spaces. +An exception to this is when the destination type is ``uintptr_t`` or +``unsigned long`` since the resulting integer value is independent +of the address space and can't be dereferenced without first casting +it back to a pointer type. + +bitwise +------- +This attribute is to be used to define new, unique integer types that +cannot be mixed with other types. In particular, you can't mix a +"bitwise" integer with a normal integer expression, and you can't even +mix it with another bitwise expression of a different type. +The integer 0 is special, though, and can be mixed with any bitwise type +since it's safe for all bitwise operations. + +Since this qualifier defines new types, it only makes sense to use +it in typedefs, which effectively makes each of these typedefs +a single "bitwise class", incompatible with any other types. + +context(*ctxt*, *entry*, *exit*) +-------------------------------- +This attribute is to be used on function declarations to specify +the function's entry and exit count for a given context. This +context can be pretty much anything that can be counted. + +Sparse will check that the function's entry and exit contexts match, and +that no path through a function is ever entered with conflicting contexts. +In particular, it is designed for doing things like matching up a "lock" +with the pairing "unlock". For example, a function doing a lock should be +annotated with an entry value of 0 and an exit value of 1, the corresponding +unlock function should use the values 1 and 0, and a function that should +only be called on some locked data, release the lock but which doesn't exit +without reacquiring the lock being, should use entry and exit values of 1. + +The first argument, *ctxt*, is an expression only used as documentation +to identify the context. Usually, what is used is a pointer to the structure +containing the context, for example, the structure protected by the lock. + +See also https://lwn.net/Articles/109066/. + +noderef +------- +This attribute is to be used on a r-value to specify it cannot be +dereferenced. A pointer so annotated is in all other aspects exactly +like a pointer but trying to actually access anything through it will +cause a warning. + +nocast +------ +This attribute is similar to ``bitwise`` but in a much weaker form. +It warns about explicit or implicit casting to different types. +However, it doesn't warn about the mixing with other types and it easily +gets lost: you can add plain integers to __nocast integer types and the +result will be plain integers. +So, it ends to be more useful for big integers that still need to act +like integers, but you want to make it much less likely that they get +truncated by mistake. For example, a 64-bit integer that you don't want +to mistakenly/silently be returned as int. + +See also `Linus' e-mail about __nocast vs __bitwise +<https://lore.kernel.org/linux-mm/CA+55aFzbhYvw7Am9EYgatpjTknBFm9eq+3jBWQHkSCUpnb3HRQ@mail.gmail.com/>`_. + +safe +---- +This attribute specifies that the object, which should be a pointer, +is defined to never be NULL or nontrapping. +It causes a warning if the object is tested in a conditional. + +force +----- +This attribute is to be used in casts to suppress warnings that would +otherwise be caused by the presence of one of these extra qualifiers. diff --git a/Documentation/index.rst b/Documentation/index.rst index 50afa558..4047343a 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -63,42 +63,28 @@ To subscribe to the list, send an email with Bugs can also be reported and tracked via the `Linux kernel's bugzilla for sparse <https://bugzilla.kernel.org/enter_bug.cgi?component=Sparse&product=Tools>`_. -User documentation ------------------- -.. toctree:: - :maxdepth: 1 - nocast-vs-bitwise - -Developer documentation ------------------------ .. toctree:: + :caption: Documentation :maxdepth: 1 - test-suite + annotations dev-options + types api IR - types + test-suite + doc-guide -How to contribute ------------------ .. toctree:: + :caption: How to contribute :maxdepth: 1 submitting-patches TODO -Documentation -------------- -.. toctree:: - :maxdepth: 1 - - doc-guide - -Release Notes -------------- .. toctree:: + :caption: Release Notes :maxdepth: 1 release-notes/index diff --git a/Documentation/nocast-vs-bitwise.md b/Documentation/nocast-vs-bitwise.md deleted file mode 100644 index 9ba5a789..00000000 --- a/Documentation/nocast-vs-bitwise.md +++ /dev/null @@ -1,43 +0,0 @@ -__nocast vs __bitwise -===================== - -`__nocast` warns about explicit or implicit casting to different types. -HOWEVER, it doesn't consider two 32-bit integers to be different -types, so a `__nocast int` type may be returned as a regular `int` -type and then the `__nocast` is lost. - -So `__nocast` on integer types is usually not that powerful. It just -gets lost too easily. It's more useful for things like pointers. It -also doesn't warn about the mixing: you can add integers to `__nocast` -integer types, and it's not really considered anything wrong. - -`__bitwise` ends up being a *stronger integer separation*. That one -doesn't allow you to mix with non-bitwise integers, so now it's much -harder to lose the type by mistake. - -So the basic rule is: - -- `__nocast` on its own tends to be more useful for *big* integers - that still need to act like integers, but you want to make it much - less likely that they get truncated by mistake. So a 64-bit integer - that you don't want to mistakenly/silently be returned as `int`, for - example. But they mix well with random integer types, so you can add - to them etc without using anything special. However, that mixing also - means that the `__nocast` really gets lost fairly easily. - -- `__bitwise` is for *unique types* that cannot be mixed with other - types, and that you'd never want to just use as a random integer (the - integer `0` is special, though, and gets silently accepted - it's - kind of like `NULL` for pointers). So `gfp_t` or the `safe endianness` - types would be `__bitwise`: you can only operate on them by doing - specific operations that know about *that* particular type. - -Generally, you want `__bitwise` if you are looking for type safety. -`__nocast` really is pretty weak. - -Reference: ----------- - -* Linus' e-mail about `__nocast` vs `__bitwise`: - - <https://marc.info/?l=linux-mm&m=133245421127324&w=2> diff --git a/Documentation/submitting-patches.md b/Documentation/submitting-patches.md index fb176ce5..6a4275c3 100644 --- a/Documentation/submitting-patches.md +++ b/Documentation/submitting-patches.md @@ -1,5 +1,5 @@ -Submitting patches: the sparse version -====================================== +Submitting patches +================== Sparse uses a patch submit process similar to the Linux Kernel [Submitting Patches](https://www.kernel.org/doc/html/v4.12/process/submitting-patches.html) diff --git a/Documentation/templates/breadcrumbs.html b/Documentation/templates/breadcrumbs.html new file mode 100644 index 00000000..4f22fa9a --- /dev/null +++ b/Documentation/templates/breadcrumbs.html @@ -0,0 +1,11 @@ +{%- extends "sphinx_rtd_theme/breadcrumbs.html" %} + +{% block breadcrumbs_aside %} + {% if hasdoc(pagename) %} + <li class="wy-breadcrumbs-aside"> + {% if show_source and has_source and sourcename %} + <a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> {{ _('View page source') }}</a> + {% endif %} + </li> + {% endif %} +{% endblock %} diff --git a/Documentation/templates/layout.html b/Documentation/templates/layout.html new file mode 100644 index 00000000..a2fe215f --- /dev/null +++ b/Documentation/templates/layout.html @@ -0,0 +1,8 @@ +{% extends "!layout.html" %} +{% block menu %} + {{ super() }} + <p class="caption"><span class="caption-text">Index</span></p> + <ul> + <li class="toctree-l1"><a class="reference internal" href="{{ pathto('genindex') }}">Index</a></li> + </ul> +{% endblock %} @@ -76,6 +76,11 @@ void get_char_constant(struct token *token, unsigned long long *val) case TOKEN_WIDE_CHAR: p = token->string->data; end = p + token->string->length - 1; + if (end == p) { + sparse_error(token->pos, "empty character constant"); + *val = 0; + return; + } break; case TOKEN_CHAR_EMBEDDED_0 ... TOKEN_CHAR_EMBEDDED_3: end = p + type - TOKEN_CHAR; @@ -102,9 +102,10 @@ static struct symbol *evaluate_string(struct expression *expr) struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL); struct expression *initstr = alloc_expression(expr->pos, EXPR_STRING); unsigned int length = expr->string->length; + struct symbol *char_type = expr->wide ? wchar_ctype : &char_ctype; sym->array_size = alloc_const_expression(expr->pos, length); - sym->bit_size = bytes_to_bits(length); + sym->bit_size = length * char_type->bit_size; sym->ctype.alignment = 1; sym->string = 1; sym->ctype.modifiers = MOD_STATIC; @@ -117,10 +118,10 @@ static struct symbol *evaluate_string(struct expression *expr) initstr->string = expr->string; array->array_size = sym->array_size; - array->bit_size = bytes_to_bits(length); - array->ctype.alignment = 1; + array->bit_size = sym->bit_size; + array->ctype.alignment = char_type->ctype.alignment; array->ctype.modifiers = MOD_STATIC; - array->ctype.base_type = &char_ctype; + array->ctype.base_type = char_type; array->examined = 1; array->evaluated = 1; @@ -405,7 +406,10 @@ static inline int is_string_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; - return type->type == SYM_ARRAY && is_byte_type(type->ctype.base_type); + if (type->type != SYM_ARRAY) + return 0; + type = type->ctype.base_type; + return is_byte_type(type) || is_wchar_type(type); } static struct symbol *bad_expr_type(struct expression *expr) @@ -2768,7 +2772,6 @@ static struct expression *handle_scalar(struct expression *e, int nested) static int handle_initializer(struct expression **ep, int nested, int class, struct symbol *ctype, unsigned long mods) { - int is_string = is_string_type(ctype); struct expression *e = *ep, *p; struct symbol *type; @@ -2802,7 +2805,7 @@ static int handle_initializer(struct expression **ep, int nested, * pathologies, so we don't need anything fancy here. */ if (e->type == EXPR_INITIALIZER) { - if (is_string) { + if (is_string_type(ctype)) { struct expression *v = NULL; int count = 0; @@ -2823,7 +2826,7 @@ static int handle_initializer(struct expression **ep, int nested, /* string */ if (is_string_literal(&e)) { /* either we are doing array of char, or we'll have to dig in */ - if (is_string) { + if (is_string_type(ctype)) { *ep = e; goto String; } @@ -2848,10 +2851,12 @@ String: *p = *e; type = evaluate_expression(p); if (ctype->bit_size != -1) { - if (ctype->bit_size + bits_in_char < type->bit_size) + struct symbol *char_type = e->wide ? wchar_ctype : &char_ctype; + unsigned int size_with_null = ctype->bit_size + char_type->bit_size; + if (size_with_null < type->bit_size) warning(e->pos, "too long initializer-string for array of char"); - else if (Winit_cstring && ctype->bit_size + bits_in_char == type->bit_size) { + else if (Winit_cstring && size_with_null == type->bit_size) { warning(e->pos, "too long initializer-string for array of char(no space for nul char)"); } @@ -3552,7 +3557,7 @@ static struct symbol *evaluate_return_expression(struct statement *stmt) fntype = current_fn->ctype.base_type; rettype = fntype->ctype.base_type; if (!rettype || rettype == &void_ctype) { - if (expr && expr->ctype != &void_ctype) + if (expr && !is_void_type(expr->ctype)) expression_error(expr, "return expression in %s function", rettype?"void":"typeless"); if (expr && Wreturn_void) warning(stmt->pos, "returning void-valued expression"); @@ -49,17 +49,14 @@ struct symbol_list *function_computed_target_list; struct statement_list *function_computed_goto_list; static struct token *statement(struct token *token, struct statement **tree); -static struct token *handle_attributes(struct token *token, struct decl_state *ctx, unsigned int keywords); +static struct token *handle_attributes(struct token *token, struct decl_state *ctx); -typedef struct token *declarator_t(struct token *, struct decl_state *); +typedef struct token *declarator_t(struct token *, struct symbol *, struct decl_state *); static declarator_t struct_specifier, union_specifier, enum_specifier, - attribute_specifier, typeof_specifier, parse_asm_declarator, - typedef_specifier, inline_specifier, auto_specifier, - register_specifier, static_specifier, extern_specifier, - thread_specifier, const_qualifier, volatile_qualifier; -static declarator_t restrict_qualifier; -static declarator_t atomic_qualifier; + attribute_specifier, typeof_specifier, + storage_specifier, thread_specifier; +static declarator_t generic_qualifier; static declarator_t autotype_specifier; static struct token *parse_if_statement(struct token *token, struct statement *stmt); @@ -116,10 +113,6 @@ enum { CInt = 0, CSInt, CUInt, CReal, }; -enum { - SNone = 0, STypedef, SAuto, SRegister, SExtern, SStatic, SForced, SMax, -}; - static void asm_modifier(struct token *token, unsigned long *mods, unsigned long mod) { if (*mods & mod) @@ -127,31 +120,20 @@ static void asm_modifier(struct token *token, unsigned long *mods, unsigned long *mods |= mod; } -static void asm_modifier_volatile(struct token *token, unsigned long *mods) -{ - asm_modifier(token, mods, MOD_VOLATILE); -} - -static void asm_modifier_inline(struct token *token, unsigned long *mods) -{ - asm_modifier(token, mods, MOD_INLINE); -} - static struct symbol_op typedef_op = { .type = KW_MODIFIER, - .declarator = typedef_specifier, + .declarator = storage_specifier, }; static struct symbol_op inline_op = { .type = KW_MODIFIER, - .declarator = inline_specifier, - .asm_modifier = asm_modifier_inline, + .declarator = generic_qualifier, + .asm_modifier = asm_modifier, }; -static declarator_t noreturn_specifier; static struct symbol_op noreturn_op = { .type = KW_MODIFIER, - .declarator = noreturn_specifier, + .declarator = generic_qualifier, }; static declarator_t alignas_specifier; @@ -162,22 +144,22 @@ static struct symbol_op alignas_op = { static struct symbol_op auto_op = { .type = KW_MODIFIER, - .declarator = auto_specifier, + .declarator = storage_specifier, }; static struct symbol_op register_op = { .type = KW_MODIFIER, - .declarator = register_specifier, + .declarator = storage_specifier, }; static struct symbol_op static_op = { .type = KW_MODIFIER|KW_STATIC, - .declarator = static_specifier, + .declarator = storage_specifier, }; static struct symbol_op extern_op = { .type = KW_MODIFIER, - .declarator = extern_specifier, + .declarator = storage_specifier, }; static struct symbol_op thread_op = { @@ -187,23 +169,23 @@ static struct symbol_op thread_op = { static struct symbol_op const_op = { .type = KW_QUALIFIER, - .declarator = const_qualifier, + .declarator = generic_qualifier, }; static struct symbol_op volatile_op = { .type = KW_QUALIFIER, - .declarator = volatile_qualifier, - .asm_modifier = asm_modifier_volatile, + .declarator = generic_qualifier, + .asm_modifier = asm_modifier, }; static struct symbol_op restrict_op = { .type = KW_QUALIFIER, - .declarator = restrict_qualifier, + .declarator = generic_qualifier, }; static struct symbol_op atomic_op = { .type = KW_QUALIFIER, - .declarator = atomic_qualifier, + .declarator = generic_qualifier, }; static struct symbol_op typeof_op = { @@ -363,7 +345,6 @@ static struct symbol_op range_op = { static struct symbol_op asm_op = { .type = KW_ASM, - .declarator = parse_asm_declarator, .statement = parse_asm_statement, .toplevel = toplevel_asm_declaration, }; @@ -482,27 +463,27 @@ static struct init_keyword { #define U(I, O,...) N("__" I,O,##__VA_ARGS__), \ N("__" I "__",O,##__VA_ARGS__) /* Storage classes */ - N("auto", &auto_op), - N("register", ®ister_op), - N("static", &static_op), - N("extern", &extern_op), + N("auto", &auto_op, .mods = MOD_AUTO), + N("register", ®ister_op, .mods = MOD_REGISTER), + N("static", &static_op, .mods = MOD_STATIC), + N("extern", &extern_op, .mods = MOD_EXTERN), N("__thread", &thread_op), N("_Thread_local", &thread_op), - A("inline", &inline_op), + A("inline", &inline_op, .mods = MOD_INLINE), /* Typedef ... */ - N("typedef", &typedef_op), + N("typedef", &typedef_op, .mods = MOD_USERTYPE), A("typeof", &typeof_op), N("__auto_type", &autotype_op), /* Type qualifiers */ - A("const", &const_op), - A("volatile", &volatile_op), - A("restrict", &restrict_op), + A("const", &const_op, .mods = MOD_CONST), + A("volatile", &volatile_op, .mods = MOD_VOLATILE), + A("restrict", &restrict_op, .mods = MOD_RESTRICT), - N("_Atomic", &atomic_op), - N("_Noreturn", &noreturn_op), + N("_Atomic", &atomic_op, .mods = MOD_ATOMIC), + N("_Noreturn", &noreturn_op, .mods = MOD_NORETURN), N("_Alignas", &alignas_op), U("attribute", &attribute_op), @@ -739,7 +720,7 @@ static struct token *struct_union_enum_specifier(enum type type, struct symbol *sym; struct position *repos; - token = handle_attributes(token, ctx, KW_ATTRIBUTE); + token = handle_attributes(token, ctx); if (token_type(token) == TOKEN_IDENT) { sym = lookup_symbol(token->ident, NS_STRUCT); if (!sym || @@ -812,12 +793,12 @@ static struct token *parse_union_declaration(struct token *token, struct symbol return struct_declaration_list(token, &sym->symbol_list); } -static struct token *struct_specifier(struct token *token, struct decl_state *ctx) +static struct token *struct_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { return struct_union_enum_specifier(SYM_STRUCT, token, ctx, parse_struct_declaration); } -static struct token *union_specifier(struct token *token, struct decl_state *ctx) +static struct token *union_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { return struct_union_enum_specifier(SYM_UNION, token, ctx, parse_union_declaration); } @@ -939,7 +920,7 @@ static struct token *parse_enum_declaration(struct token *token, struct symbol * struct symbol *sym; // FIXME: only 'deprecated' should be accepted - next = handle_attributes(next, &ctx, KW_ATTRIBUTE); + next = handle_attributes(next, &ctx); if (match_op(next, '=')) { next = constant_expression(next->next, &expr); @@ -1051,7 +1032,7 @@ static struct token *parse_enum_declaration(struct token *token, struct symbol * return token; } -static struct token *enum_specifier(struct token *token, struct decl_state *ctx) +static struct token *enum_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { struct token *ret = struct_union_enum_specifier(SYM_ENUM, token, ctx, parse_enum_declaration); struct ctype *ctype = &ctx->ctype.base_type->ctype; @@ -1064,15 +1045,15 @@ static struct token *enum_specifier(struct token *token, struct decl_state *ctx) static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype); -static struct token *typeof_specifier(struct token *token, struct decl_state *ctx) +static struct token *typeof_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { - struct symbol *sym; if (!match_op(token, '(')) { sparse_error(token->pos, "expected '(' after typeof"); return token; } if (lookup_type(token->next)) { + struct symbol *sym; token = typename(token->next, &sym, NULL); ctx->ctype.base_type = sym->ctype.base_type; apply_ctype(token->pos, &sym->ctype, &ctx->ctype); @@ -1090,7 +1071,7 @@ 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) +static struct token *autotype_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { ctx->ctype.base_type = &autotype_ctype; ctx->autotype = 1; @@ -1133,7 +1114,7 @@ static struct token *attribute_aligned(struct token *token, struct symbol *attr, static void apply_mod(struct position *pos, unsigned long *mods, unsigned long mod) { if (*mods & mod & ~MOD_DUP_OK) - warning(*pos, "duplicate %s", modifier_string(mod)); + warning(*pos, "duplicate %s", modifier_name(mod)); *mods |= mod; } @@ -1144,7 +1125,7 @@ static void apply_qualifier(struct position *pos, struct ctype *ctx, unsigned lo static struct token *attribute_modifier(struct token *token, struct symbol *attr, struct decl_state *ctx) { - apply_qualifier(&token->pos, &ctx->ctype, attr->ctype.modifiers); + apply_mod(&token->pos, &ctx->ctype.modifiers, attr->ctype.modifiers); return token; } @@ -1270,7 +1251,7 @@ static struct token *attribute_mode(struct token *token, struct symbol *attr, st token = expect(token, '(', "after mode attribute"); if (token_type(token) == TOKEN_IDENT) { struct symbol *mode = lookup_keyword(token->ident, NS_KEYWORD); - if (mode && mode->op->type == KW_MODE) + if (mode && mode->op->type & KW_MODE) ctx->mode = mode->op; else sparse_error(token->pos, "unknown mode attribute %s", show_ident(token->ident)); @@ -1339,7 +1320,7 @@ static struct token *recover_unknown_attribute(struct token *token) return token; } -static struct token *attribute_specifier(struct token *token, struct decl_state *ctx) +static struct token *attribute_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { token = expect(token, '(', "after attribute"); token = expect(token, '(', "after attribute"); @@ -1361,89 +1342,39 @@ static struct token *attribute_specifier(struct token *token, struct decl_state return token; } -static const char *storage_class[] = -{ - [STypedef] = "typedef", - [SAuto] = "auto", - [SExtern] = "extern", - [SStatic] = "static", - [SRegister] = "register", - [SForced] = "[force]" -}; - static unsigned long decl_modifiers(struct decl_state *ctx) { - static unsigned long mod[SMax] = - { - [SAuto] = MOD_AUTO, - [SExtern] = MOD_EXTERN, - [SStatic] = MOD_STATIC, - [SRegister] = MOD_REGISTER - }; unsigned long mods = ctx->ctype.modifiers & MOD_DECLARE; ctx->ctype.modifiers &= ~MOD_DECLARE; - return mod[ctx->storage_class] | mods; + return ctx->storage_class | mods; } -static void set_storage_class(struct position *pos, struct decl_state *ctx, int class) +static struct token *storage_specifier(struct token *next, struct symbol *sym, struct decl_state *ctx) { int is_tls = ctx->ctype.modifiers & MOD_TLS; - /* __thread can be used alone, or with extern or static */ - if (is_tls && (class != SStatic && class != SExtern)) { - sparse_error(*pos, "__thread can only be used alone, or with " - "extern or static"); - return; - } + unsigned long class = sym->ctype.modifiers; + const char *storage = modifier_name(class); - if (!ctx->storage_class) { + /* __thread can be used alone, or with extern or static */ + if (is_tls && (class & ~(MOD_STATIC|MOD_EXTERN))) + sparse_error(next->pos, "__thread cannot be used with '%s'", storage); + else if (!ctx->storage_class) ctx->storage_class = class; - return; - } - if (ctx->storage_class == class) - sparse_error(*pos, "duplicate %s", storage_class[class]); + else if (ctx->storage_class == class) + sparse_error(next->pos, "duplicate %s", storage); else - sparse_error(*pos, "multiple storage classes"); -} - -static struct token *typedef_specifier(struct token *next, struct decl_state *ctx) -{ - set_storage_class(&next->pos, ctx, STypedef); - return next; -} - -static struct token *auto_specifier(struct token *next, struct decl_state *ctx) -{ - set_storage_class(&next->pos, ctx, SAuto); - return next; -} - -static struct token *register_specifier(struct token *next, struct decl_state *ctx) -{ - set_storage_class(&next->pos, ctx, SRegister); - return next; -} - -static struct token *static_specifier(struct token *next, struct decl_state *ctx) -{ - set_storage_class(&next->pos, ctx, SStatic); - return next; -} - -static struct token *extern_specifier(struct token *next, struct decl_state *ctx) -{ - set_storage_class(&next->pos, ctx, SExtern); + sparse_error(next->pos, "multiple storage classes"); return next; } -static struct token *thread_specifier(struct token *next, struct decl_state *ctx) +static struct token *thread_specifier(struct token *next, struct symbol *sym, struct decl_state *ctx) { /* This GCC extension can be used alone, or with extern or static */ - if (!ctx->storage_class || ctx->storage_class == SStatic - || ctx->storage_class == SExtern) { + if (!(ctx->storage_class & ~(MOD_STATIC|MOD_EXTERN))) { apply_qualifier(&next->pos, &ctx->ctype, MOD_TLS); } else { - sparse_error(next->pos, "__thread can only be used alone, or " - "with extern or static"); + sparse_error(next->pos, "__thread cannot be used with '%s'", + modifier_name(ctx->storage_class)); } return next; @@ -1451,23 +1382,11 @@ static struct token *thread_specifier(struct token *next, struct decl_state *ctx static struct token *attribute_force(struct token *token, struct symbol *attr, struct decl_state *ctx) { - set_storage_class(&token->pos, ctx, SForced); + ctx->forced = 1; return token; } -static struct token *inline_specifier(struct token *next, struct decl_state *ctx) -{ - apply_qualifier(&next->pos, &ctx->ctype, MOD_INLINE); - return next; -} - -static struct token *noreturn_specifier(struct token *next, struct decl_state *ctx) -{ - apply_qualifier(&next->pos, &ctx->ctype, MOD_NORETURN); - return next; -} - -static struct token *alignas_specifier(struct token *token, struct decl_state *ctx) +static struct token *alignas_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { int alignment = 0; @@ -1502,27 +1421,9 @@ static struct token *alignas_specifier(struct token *token, struct decl_state *c return token; } -static struct token *const_qualifier(struct token *next, struct decl_state *ctx) -{ - apply_qualifier(&next->pos, &ctx->ctype, MOD_CONST); - return next; -} - -static struct token *volatile_qualifier(struct token *next, struct decl_state *ctx) -{ - apply_qualifier(&next->pos, &ctx->ctype, MOD_VOLATILE); - return next; -} - -static struct token *restrict_qualifier(struct token *next, struct decl_state *ctx) -{ - apply_qualifier(&next->pos, &ctx->ctype, MOD_RESTRICT); - return next; -} - -static struct token *atomic_qualifier(struct token *next, struct decl_state *ctx) +static struct token *generic_qualifier(struct token *next, struct symbol *sym, struct decl_state *ctx) { - apply_qualifier(&next->pos, &ctx->ctype, MOD_ATOMIC); + apply_qualifier(&next->pos, &ctx->ctype, sym->ctype.modifiers); return next; } @@ -1600,16 +1501,14 @@ struct symbol *ctype_integer(int size, int want_unsigned) static struct token *handle_qualifiers(struct token *t, struct decl_state *ctx) { while (token_type(t) == TOKEN_IDENT) { - struct symbol *s = lookup_symbol(t->ident, NS_TYPEDEF); + struct symbol *s = lookup_keyword(t->ident, NS_TYPEDEF); if (!s) break; - if (s->type != SYM_KEYWORD) - break; if (!(s->op->type & (KW_ATTRIBUTE | KW_QUALIFIER))) break; t = t->next; if (s->op->declarator) - t = s->op->declarator(t, ctx); + t = s->op->declarator(t, s, ctx); } return t; } @@ -1661,7 +1560,7 @@ static struct token *declaration_specifiers(struct token *token, struct decl_sta } token = token->next; if (s->op->declarator) // Note: this eats attributes - token = s->op->declarator(token, ctx); + token = s->op->declarator(token, s, ctx); if (s->op->type & KW_EXACT) { ctx->ctype.base_type = s->ctype.base_type; ctx->ctype.modifiers |= s->ctype.modifiers; @@ -1721,6 +1620,40 @@ static struct token *parameter_type_list(struct token *, struct symbol *); static struct token *identifier_list(struct token *, struct symbol *); static struct token *declarator(struct token *token, struct decl_state *ctx); +static struct token *handle_asm_name(struct token *token, struct decl_state *ctx) +{ + struct expression *expr; + struct symbol *keyword; + + if (token_type(token) != TOKEN_IDENT) + return token; + keyword = lookup_keyword(token->ident, NS_KEYWORD); + if (!keyword) + return token; + if (!(keyword->op->type & KW_ASM)) + return token; + + token = token->next; + token = expect(token, '(', "after asm"); + token = string_expression(token, &expr, "asm name"); + token = expect(token, ')', "after asm"); + return token; +} + +/// +// test if @token is '__attribute__' (or one of its variant) +static bool match_attribute(struct token *token) +{ + struct symbol *sym; + + if (token_type(token) != TOKEN_IDENT) + return false; + sym = lookup_keyword(token->ident, NS_TYPEDEF); + if (!sym) + return false; + return sym->op->type & KW_ATTRIBUTE; +} + static struct token *skip_attribute(struct token *token) { token = token->next; @@ -1742,15 +1675,7 @@ static struct token *skip_attribute(struct token *token) static struct token *skip_attributes(struct token *token) { - struct symbol *keyword; - for (;;) { - if (token_type(token) != TOKEN_IDENT) - break; - keyword = lookup_keyword(token->ident, NS_KEYWORD | NS_TYPEDEF); - if (!keyword || keyword->type != SYM_KEYWORD) - break; - if (!(keyword->op->type & KW_ATTRIBUTE)) - break; + while (match_attribute(token)) { token = expect(token->next, '(', "after attribute"); token = expect(token, '(', "after attribute"); while (token_type(token) == TOKEN_IDENT) { @@ -1765,20 +1690,10 @@ static struct token *skip_attributes(struct token *token) return token; } -static struct token *handle_attributes(struct token *token, struct decl_state *ctx, unsigned int keywords) +static struct token *handle_attributes(struct token *token, struct decl_state *ctx) { - struct symbol *keyword; - for (;;) { - if (token_type(token) != TOKEN_IDENT) - break; - keyword = lookup_keyword(token->ident, NS_KEYWORD | NS_TYPEDEF); - if (!keyword || keyword->type != SYM_KEYWORD) - break; - if (!(keyword->op->type & keywords)) - break; - token = keyword->op->declarator(token->next, ctx); - keywords &= KW_ATTRIBUTE; - } + while (match_attribute(token)) + token = attribute_specifier(token->next, NULL, ctx); return token; } @@ -1864,8 +1779,7 @@ static struct token *direct_declarator(struct token *token, struct decl_state *c is_nested(token, &next, ctx->prefer_abstract)) { struct symbol *base_type = ctype->base_type; if (token->next != next) - next = handle_attributes(token->next, ctx, - KW_ATTRIBUTE); + next = handle_attributes(token->next, ctx); token = declarator(next, ctx); token = expect(token, ')', "in nested declarator"); while (ctype->base_type != base_type) @@ -1988,7 +1902,7 @@ static struct token *declaration_list(struct token *token, struct symbol_list ** if (match_op(token, ':')) token = handle_bitfield(token, &ctx); - token = handle_attributes(token, &ctx, KW_ATTRIBUTE); + token = handle_attributes(token, &ctx); apply_modifiers(token->pos, &ctx); decl->ctype = ctx.ctype; @@ -2028,19 +1942,19 @@ static struct token *parameter_declaration(struct token *token, struct symbol *s token = declaration_specifiers(token, &ctx); ctx.ident = &sym->ident; token = declarator(token, &ctx); - token = handle_attributes(token, &ctx, KW_ATTRIBUTE); + token = handle_attributes(token, &ctx); apply_modifiers(token->pos, &ctx); sym->ctype = ctx.ctype; sym->ctype.modifiers |= decl_modifiers(&ctx); sym->endpos = token->pos; - sym->forced_arg = ctx.storage_class == SForced; + sym->forced_arg = ctx.forced; return token; } struct token *typename(struct token *token, struct symbol **p, int *forced) { struct decl_state ctx = {.prefer_abstract = 1}; - int class; + unsigned long class; struct symbol *sym = alloc_symbol(token->pos, SYM_NODE); *p = sym; token = declaration_specifiers(token, &ctx); @@ -2049,16 +1963,11 @@ struct token *typename(struct token *token, struct symbol **p, int *forced) sym->ctype = ctx.ctype; sym->endpos = token->pos; class = ctx.storage_class; - if (forced) { - *forced = 0; - if (class == SForced) { - *forced = 1; - class = 0; - } - } + if (forced) + *forced = ctx.forced; if (class) - warning(sym->pos, "storage class in typename (%s %s)", - storage_class[class], show_typename(sym)); + warning(sym->pos, "storage class in typename (%s%s)", + modifier_string(class), show_typename(sym)); return token; } @@ -2127,8 +2036,8 @@ static struct token *parse_asm_statement(struct token *token, struct statement * stmt->type = STMT_ASM; while (token_type(token) == TOKEN_IDENT) { struct symbol *s = lookup_keyword(token->ident, NS_TYPEDEF); - if (s && s->op && s->op->asm_modifier) - s->op->asm_modifier(token, &mods); + if (s && s->op->asm_modifier) + s->op->asm_modifier(token, &mods, s->ctype.modifiers); else if (token->ident == &goto_ident) asm_modifier(token, &mods, MOD_ASM_GOTO); token = token->next; @@ -2147,15 +2056,6 @@ static struct token *parse_asm_statement(struct token *token, struct statement * return expect(token, ';', "at end of asm-statement"); } -static struct token *parse_asm_declarator(struct token *token, struct decl_state *ctx) -{ - struct expression *expr; - token = expect(token, '(', "after asm"); - token = string_expression(token, &expr, "inline asm"); - token = expect(token, ')', "after asm"); - return token; -} - static struct token *parse_static_assert(struct token *token, struct symbol_list **unused) { struct expression *cond = NULL, *message = NULL; @@ -2537,7 +2437,7 @@ static struct token *handle_label_attributes(struct token *token, struct symbol { struct decl_state ctx = { }; - token = handle_attributes(token, &ctx, KW_ATTRIBUTE); + token = handle_attributes(token, &ctx); label->label_modifiers = ctx.ctype.modifiers; return token; } @@ -2997,7 +2897,8 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis saved = ctx.ctype; token = declarator(token, &ctx); - token = handle_attributes(token, &ctx, KW_ATTRIBUTE | KW_ASM); + token = handle_asm_name(token, &ctx); + token = handle_attributes(token, &ctx); apply_modifiers(token->pos, &ctx); decl->ctype = ctx.ctype; @@ -3011,7 +2912,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis } /* type define declaration? */ - is_typedef = ctx.storage_class == STypedef; + is_typedef = ctx.storage_class == MOD_USERTYPE; /* Typedefs don't have meaningful storage */ if (is_typedef) @@ -3119,9 +3020,10 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis ident = NULL; decl = alloc_symbol(token->pos, SYM_NODE); ctx.ctype = saved; - token = handle_attributes(token, &ctx, KW_ATTRIBUTE); + token = handle_attributes(token, &ctx); token = declarator(token, &ctx); - token = handle_attributes(token, &ctx, KW_ATTRIBUTE | KW_ASM); + token = handle_asm_name(token, &ctx); + token = handle_attributes(token, &ctx); apply_modifiers(token->pos, &ctx); decl->ctype = ctx.ctype; decl->ctype.modifiers |= mod; diff --git a/show-parse.c b/show-parse.c index 51a15191..0546a7b9 100644 --- a/show-parse.c +++ b/show-parse.c @@ -70,7 +70,7 @@ static void do_debug_symbol(struct symbol *sym, int indent) if (!sym) return; - fprintf(stderr, "%.*s%s%3d:%lu %s %s (as: %s) %p (%s:%d:%d) %s\n", + fprintf(stderr, "%.*s%s%3d:%lu %s%s (as: %s) %p (%s:%d:%d) %s\n", indent, indent_string, typestr[sym->type], sym->bit_size, sym->ctype.alignment, modifier_string(sym->ctype.modifiers), show_ident(sym->ident), @@ -107,7 +107,7 @@ void debug_symbol(struct symbol *sym) * Symbol type printout. The type system is by far the most * complicated part of C - everything else is trivial. */ -const char *modifier_string(unsigned long mod) +static const char *show_modifiers(unsigned long mod, int term) { static char buffer[100]; int len = 0; @@ -155,10 +155,26 @@ const char *modifier_string(unsigned long mod) buffer[len++] = ' '; } } + if (len && !term) // strip the trailing space + --len; buffer[len] = 0; return buffer; } +/// +// show the modifiers, terminated by a space if not empty +const char *modifier_string(unsigned long mod) +{ + return show_modifiers(mod, 1); +} + +/// +// show the modifiers, without an ending space +const char *modifier_name(unsigned long mod) +{ + return show_modifiers(mod, 0); +} + static void show_struct_member(struct symbol *sym) { printf("\t%s:%d:%ld at offset %ld.%d", show_ident(sym->ident), sym->bit_size, sym->ctype.alignment, sym->offset, sym->bit_offset); @@ -307,6 +307,29 @@ void merge_type(struct symbol *sym, struct symbol *base_type) merge_type(sym, sym->ctype.base_type); } +static bool is_wstring_expr(struct expression *expr) +{ + while (expr) { + switch (expr->type) { + case EXPR_STRING: + return 1; + case EXPR_INITIALIZER: + if (expression_list_size(expr->expr_list) != 1) + return 0; + expr = first_expression(expr->expr_list); + break; + case EXPR_PREOP: + if (expr->op == '(') { + expr = expr->unop; + break; + } + default: + return 0; + } + } + return 0; +} + static int count_array_initializer(struct symbol *t, struct expression *expr) { int nr = 0; @@ -321,6 +344,8 @@ static int count_array_initializer(struct symbol *t, struct expression *expr) */ if (t->ctype.base_type == &int_type && t->rank == -2) is_char = 1; + else if (t == wchar_ctype && is_wstring_expr(expr)) + is_char = 1; switch (expr->type) { case EXPR_INITIALIZER: { @@ -108,8 +108,10 @@ struct decl_state { struct ident **ident; struct symbol_op *mode; unsigned long f_modifiers; // function attributes - unsigned char prefer_abstract, storage_class; + unsigned long storage_class; + unsigned char prefer_abstract; unsigned char autotype; + unsigned char forced; }; struct pseudo; @@ -124,12 +126,12 @@ struct symbol_op { struct pseudo *(*linearize)(struct entrypoint *, struct expression *); /* keywords */ - struct token *(*declarator)(struct token *token, struct decl_state *ctx); + struct token *(*declarator)(struct token *token, struct symbol *, struct decl_state *ctx); struct token *(*statement)(struct token *token, struct statement *stmt); struct token *(*toplevel)(struct token *token, struct symbol_list **list); struct token *(*attribute)(struct token *token, struct symbol *attr, struct decl_state *ctx); struct symbol *(*to_mode)(struct symbol *); - void (*asm_modifier)(struct token *token, unsigned long *mods); + void (*asm_modifier)(struct token *token, unsigned long *mods, unsigned long mod); int test, set, class; }; @@ -328,6 +330,7 @@ extern void init_linearized_builtins(int stream); extern void init_ctype(void); extern struct symbol *alloc_symbol(struct position, int type); extern void show_type(struct symbol *); +extern const char *modifier_name(unsigned long mod); extern const char *modifier_string(unsigned long mod); extern void show_symbol(struct symbol *); extern int show_symbol_expr_init(struct symbol *sym); @@ -430,6 +433,13 @@ static inline int is_byte_type(struct symbol *type) return type->bit_size == bits_in_char && type->type != SYM_BITFIELD; } +static inline int is_wchar_type(struct symbol *type) +{ + if (type->type == SYM_NODE) + type = type->ctype.base_type; + return type == wchar_ctype; +} + static inline int is_void_type(struct symbol *type) { if (type->type == SYM_NODE) @@ -625,12 +625,7 @@ static int eat_string(int next, stream_t *stream, enum token_type type) warning(stream_pos(stream), "string too long (%d bytes, %d bytes max)", len, MAX_STRING); len = MAX_STRING; } - if (delim == '\'' && len <= 4) { - if (len == 0) { - sparse_error(stream_pos(stream), - "empty character constant"); - return nextchar(stream); - } + if (delim == '\'' && len && len <= 4) { token_type(token) = type + len; memset(buffer + len, '\0', 4 - len); memcpy(token->embedded, buffer, 4); diff --git a/validation/empty-char-constant.c b/validation/empty-char-constant.c new file mode 100644 index 00000000..f674037a --- /dev/null +++ b/validation/empty-char-constant.c @@ -0,0 +1,9 @@ +static int a = ''; + +/* + * check-name: empty-char-constant + * + * check-error-start +empty-char-constant.c:1:16: error: empty character constant + * check-error-end + */ diff --git a/validation/init-wstring.c b/validation/init-wstring.c new file mode 100644 index 00000000..d9ce3b3c --- /dev/null +++ b/validation/init-wstring.c @@ -0,0 +1,40 @@ +static const __WCHAR_TYPE__ ok0[] = L"abc"; +_Static_assert(sizeof(ok0) == 4 * sizeof(__WCHAR_TYPE__)); +static const __WCHAR_TYPE__ ok1[] = (L"abc"); +_Static_assert(sizeof(ok1) == 4 * sizeof(__WCHAR_TYPE__)); +static const __WCHAR_TYPE__ ok2[] = { L"abc" }; +_Static_assert(sizeof(ok2) == 4 * sizeof(__WCHAR_TYPE__)); + +static const __WCHAR_TYPE__ ok3[4] = L"abc"; +_Static_assert(sizeof(ok3) == 4 * sizeof(__WCHAR_TYPE__)); +static const __WCHAR_TYPE__ ok4[4] = (L"abc"); +_Static_assert(sizeof(ok4) == 4 * sizeof(__WCHAR_TYPE__)); +static const __WCHAR_TYPE__ ok5[4] = { (L"abc") }; +_Static_assert(sizeof(ok5) == 4 * sizeof(__WCHAR_TYPE__)); + +static const __WCHAR_TYPE__ ok6[7] = L"abc"; +_Static_assert(sizeof(ok6) == 7 * sizeof(__WCHAR_TYPE__)); +static const __WCHAR_TYPE__ ok7[7] = (L"abc"); +_Static_assert(sizeof(ok7) == 7 * sizeof(__WCHAR_TYPE__)); +static const __WCHAR_TYPE__ ok8[7] = { (L"abc") }; +_Static_assert(sizeof(ok8) == 7 * sizeof(__WCHAR_TYPE__)); + +static const __WCHAR_TYPE__ *ptr[] = { L"abc" }; +_Static_assert(sizeof(ptr) == sizeof(void *)); + +static struct s { + const __WCHAR_TYPE__ str[4]; +} str = { L"xyz" }; + +static const __WCHAR_TYPE__ ko3[3] = L"abc"; +static const __WCHAR_TYPE__ ko2[2] = L"abc"; + +/* + * check-name: init-wstring + * check-command: sparse -Winit-cstring $file + * + * check-error-start +init-wstring.c:29:38: warning: too long initializer-string for array of char(no space for nul char) +init-wstring.c:30:38: warning: too long initializer-string for array of char + * check-error-end + */ diff --git a/validation/init_cstring.c b/validation/init_cstring.c index 00eca20a..bac814e4 100644 --- a/validation/init_cstring.c +++ b/validation/init_cstring.c @@ -1,11 +1,13 @@ static struct alpha { char a[2]; } x = { .a = "ab" }; +static const char str[2] = "abc"; /* * check-name: -Winit-cstring option * * check-command: sparse -Winit-cstring $file * check-error-start init_cstring.c:3:14: warning: too long initializer-string for array of char(no space for nul char) +init_cstring.c:4:28: warning: too long initializer-string for array of char * check-error-end */ diff --git a/validation/linear/bug-assign-op0.c b/validation/linear/bug-assign-op0.c index b351bb51..3a2bef3c 100644 --- a/validation/linear/bug-assign-op0.c +++ b/validation/linear/bug-assign-op0.c @@ -10,7 +10,7 @@ unsigned int lsr(unsigned int u) return u; } -int divr(int s, unsigned long u) +int divr(int s, unsigned long long u) { extern int use(int, unsigned); int t = s; @@ -19,25 +19,25 @@ int divr(int s, unsigned long u) return use(s, u); } -int sdivul(int s, unsigned long u) +int sdivul(int s, unsigned long long u) { s /= u; // divu return s; } -unsigned int udivsl(unsigned int u, long s) +unsigned int udivsl(unsigned int u, long long s) { u /= s; // divs return u; } -int uldivs(int s, unsigned long u) +int uldivs(int s, unsigned long long u) { u /= s; // divu return u; } -unsigned int sldivu(unsigned int u, long s) +unsigned int sldivu(unsigned int u, long long s) { s /= u; // divs return s; diff --git a/validation/preprocessor/empty-char-constant.c b/validation/preprocessor/empty-char-constant.c new file mode 100644 index 00000000..2c248159 --- /dev/null +++ b/validation/preprocessor/empty-char-constant.c @@ -0,0 +1,13 @@ +#if 0 + '' +#endif + +/* + * check-name: empty-char-constant + * check-command: sparse -E $file + * + * check-output-start + + + * check-output-end + */ |