aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Documentation/annotations.rst85
-rw-r--r--Documentation/index.rst28
-rw-r--r--Documentation/nocast-vs-bitwise.md43
-rw-r--r--Documentation/submitting-patches.md4
-rw-r--r--Documentation/templates/breadcrumbs.html11
-rw-r--r--Documentation/templates/layout.html8
-rw-r--r--char.c5
-rw-r--r--evaluate.c27
-rw-r--r--parse.c336
-rw-r--r--show-parse.c20
-rw-r--r--symbol.c25
-rw-r--r--symbol.h16
-rw-r--r--tokenize.c7
-rw-r--r--validation/empty-char-constant.c9
-rw-r--r--validation/init-wstring.c40
-rw-r--r--validation/init_cstring.c2
-rw-r--r--validation/linear/bug-assign-op0.c10
-rw-r--r--validation/preprocessor/empty-char-constant.c13
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 %}
diff --git a/char.c b/char.c
index f26b2a80..730ae3f5 100644
--- a/char.c
+++ b/char.c
@@ -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;
diff --git a/evaluate.c b/evaluate.c
index 9990b57b..63a9390b 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -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");
diff --git a/parse.c b/parse.c
index 5f587ce6..faf8fcb0 100644
--- a/parse.c
+++ b/parse.c
@@ -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", &register_op),
- N("static", &static_op),
- N("extern", &extern_op),
+ N("auto", &auto_op, .mods = MOD_AUTO),
+ N("register", &register_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);
diff --git a/symbol.c b/symbol.c
index 6fcb1b15..7f0c8558 100644
--- a/symbol.c
+++ b/symbol.c
@@ -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: {
diff --git a/symbol.h b/symbol.h
index 8e7a2860..873d69fc 100644
--- a/symbol.h
+++ b/symbol.h
@@ -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)
diff --git a/tokenize.c b/tokenize.c
index ca842f52..ea710543 100644
--- a/tokenize.c
+++ b/tokenize.c
@@ -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
+ */