lclint-interest message 84

From ok@yallara.cs.rmit.EDU.AU Fri Jul  5 13:09:39 1996
Date: Fri, 5 Jul 1996 12:19:13 +1000 (EST)
From: Richard A OKeefe 
To: lclint-interest@larch.lcs.mit.edu, lclint@larch.lcs.mit.edu
Subject: Formal array size check request

I've been using lclint to help me mark student code.
There is one thing that the students are rather confused about
(or rather, the students actually have it right, but C is confused).

If they have an array,
	char line[COLUMNS];
and they want to pass that array to a function foo,
they _will_ insist on declaring
	void foo(char line[COLUMNS]);
in the prototype and
	void foo(char line[COLUMNS]) { ... }
in the definition.

Problem 1:

    It is a bad idea to put formal parameter names in prototypes.
    To an audience concerned with writing correct code, I don't need
    to explain why.

    I can find no lclint flag to warn about this.

    Sometimes people put "_" on prototype formal parameter names, which
    is safer.  Given the prefix checking that is already built into
    lc lint, a new flag in the spirit of lclint would be

	protoformalprefix	- if set, every formal parameter name
				- in a prototype must begin with this
				- Default:  "".


Problem 2:

    Many students are putting prototypes inside functions.
    I don't suppose I need to explain the dangers of this either.
    This is one of the checks I still have to use GCC for: -Wnested-externs
    There are actually two separable things:

	'extern' declarations inside functions,
	whether they are function declarations or prototypes, or variables.

	function declarations or prototypes inside functions,
	whether they are implicitly extern, explicitly extern,
	or explicitly static

    The name of the GCC option suggests only the former.  Both things are
    bad, however, so it might be as well to stick with a single option
    covering both.

	nestedexterns		- if set, warn about function declarations
				- or prototypes of any sort and about
				- extern declarations of any sort, within
				- blocks.
				- Default: +

Problem 3:

    The constant-expression-opt in an array direct-declarator of
    a parameter-declaration (or a declaration, in an old-style
    function definition) is ignored, if present.

	void foo(char line[80]);		is identical to
	void foo(char line[967]);		is identical to
	void foo(char line[]);			is identical to
	void foo(char *line);

    and the same is true in a function definition.  (Of course, if there
    is a series of [sizes] then only the first array size is ignored;
    that's the one I'm talking about.)

    It is arguable that including the size is good documentation.
    However, there are several problems.

    (a) Whatever the number in the brackets, the parameter IS NOT AN ARRAY,
	it's a pointer.  sizeof may legally be applied to the parameter,
	and the result will be the size of the appropriate pointer, the
	number in the brackets being completely ignored.

    (b) Students expect the compiler to _check_ that actual parameters are
	compatible with the declaration of the formal parameter, but it
	won't.

    Here is a sample program, together with the output of several checkers:

    /* begin foo.c */
    #include 

    static void
    foo(char line[80]) {
	size_t i;

	for (i = 0; i < sizeof line; i++) line[i] = ' ';
    }

    int
    main(void) {
	char line[15];

	foo(line);
	return 0;
    }
    /* end foo.c */
    y% cc foo.c
    y% lint foo.c
    y% glint foo.c
    y% gcc -O2 -Wall foo.c
    y% lclint foo.c
    LCLint 2.1b --- 07 May 96

    < preprocessing >
    < checking foo.c >
    < global checks >

    Finished LCLint checking --- no code errors found
    17 source (18 before pre-processing) lines in 0.14 s.

    lclint +strict complains about four things, caused by lack of
    annotations.  It does not detect either of the two mistakes present.

    (a) The student who wrote the code this example is based on
	expected sizeof line to be 80.  In fact it was 4.
    (b) The student expected the compiler to check that actual parameters
	would have 80 elements; all the tools were perfectly happy with
	an array of only 15 elements being passed.

    There is no way of "fixing" the first problem, given the definition of C.
    The best we can do is to report an error if sizeof is applied to a formal
    parameter declared like an array:

	sizeofformalarray	- if set, warn whenever 'sizeof' is applied
				- to a formal parameter identifier that was
				- declared like an array instead of the
				- pointer it really is.
				- Default: +

    In an ideal world, lclint would do the conformance checks that students
    familiar with Ada and Pascal expect it to do on these parameters.  I've
    no idea how hard that would be.  Even if it did, I would still think it
    a good idea to avoid such parameter declarations.  So

	formalarray		- if set, warn whenever a formal pointer
				- parameter is declared using array syntax.
				- Default: (weak, -), (standard..., +)

    There is room for disagreement about whether "char line[]" should
    provoke a warning as well as "char line[80]".  I suspect this option
    would be welcomed by more people if it didn't, and I could live with
    that.

Problem 4:

    I've just proposed four new flags:

	protoformalprefix
	nestedexterns
	sizeofformalarray
	formalarray

    The problem is that I find lotsofwordsruntogether withoutevenacaseshift
    hard to read, and therefore hard to type.

    How would it be if lclint were always to squish underscores and hyphens
    out of flag names, so that these flags could also be written as

	proto_formal_prefix
	nested-externs
	sizeof_formal_array
	formal-array



Previous Message Next Message Archive Summary LCLint Home Page David Evans
University of Virginia, Computer Science
evans@cs.virginia.edu