Give an error for declarations that do not declare anything.

This enforces the constraint from C17 section 6.7 p2 that declarations "shall declare at least a declarator (other than the parameters of a function or the members of a structure or union), a tag, or the members of an enumeration."

Somewhat relaxed rules are used for enums in the default loose type checking mode, similar to what GCC and Clang do.
This commit is contained in:
Stephen Heumann 2022-10-06 20:42:11 -05:00
parent 995ded07a5
commit f263066f61
2 changed files with 29 additions and 4 deletions

View File

@ -196,6 +196,7 @@ var
compoundLiteralNumber: integer; {number of compound literal} compoundLiteralNumber: integer; {number of compound literal}
compoundLiteralToAllocate: identPtr; {compound literal that needs space allocated} compoundLiteralToAllocate: identPtr; {compound literal that needs space allocated}
vaInfoLLN: integer; {label number of internal va info (0 for none)} vaInfoLLN: integer; {label number of internal va info (0 for none)}
declaredTagOrEnumConst: boolean; {was a tag or enum const declared?}
{parameter processing variables} {parameter processing variables}
{------------------------------} {------------------------------}
@ -2755,6 +2756,8 @@ procedure DeclarationSpecifiers (var declSpecifiers: declSpecifiersRecord;
{ referencing a forward struct/union? } { referencing a forward struct/union? }
{ skipDeclarator - for enum,struct,union with no } { skipDeclarator - for enum,struct,union with no }
{ declarator } { declarator }
{ declaredTagOrEnumConst - set if a tag or an enum const }
{ is declared (otherwise unchanged) }
label 1,2,3; label 1,2,3;
@ -3178,9 +3181,16 @@ while token.kind in allowedTokens do begin
end {if} end {if}
else else
if (variable <> nil) and (variable^.itype^.kind = enumType) then if (variable <> nil) and (variable^.itype^.kind = enumType) then
goto 1 begin
else if not looseTypeChecks then if looseTypeChecks then
Error(171); declaredTagOrEnumConst := true;
goto 1;
end {if}
else begin
declaredTagOrEnumConst := true;
if not looseTypeChecks then
Error(171);
end; {else}
tPtr := pointer(Malloc(sizeof(typeRecord))); tPtr := pointer(Malloc(sizeof(typeRecord)));
tPtr^.size := cgWordSize; tPtr^.size := cgWordSize;
tPtr^.saveDisp := 0; tPtr^.saveDisp := 0;
@ -3193,6 +3203,7 @@ while token.kind in allowedTokens do begin
Error(9); Error(9);
enumVal := 0; {set the default value} enumVal := 0; {set the default value}
if token.kind = lbracech then begin if token.kind = lbracech then begin
declaredTagOrEnumConst := true;
NextToken; {skip the '{'} NextToken; {skip the '{'}
repeat {declare the enum constants} repeat {declare the enum constants}
tPtr := pointer(Malloc(sizeof(typeRecord))); tPtr := pointer(Malloc(sizeof(typeRecord)));
@ -3263,6 +3274,10 @@ while token.kind in allowedTokens do begin
structPtr := FindSymbol(token, tagSpace, true, true); structPtr := FindSymbol(token, tagSpace, true, true);
ttoken := token; {record the structure name} ttoken := token; {record the structure name}
NextToken; {skip the structure name} NextToken; {skip the structure name}
if (token.kind = lbracech) or
((token.kind = semicolonch) and (myDeclarationModifiers = []))
then
declaredTagOrEnumConst := true;
if structPtr = nil then begin {if the name hasn't been defined then...} if structPtr = nil then begin {if the name hasn't been defined then...}
if token.kind <> lbracech then if token.kind <> lbracech then
if (token.kind <> semicolonch) or if (token.kind <> semicolonch) or
@ -3289,19 +3304,23 @@ while token.kind in allowedTokens do begin
structPtr := NewSymbol(ttoken.name, structTypePtr, ident, structPtr := NewSymbol(ttoken.name, structTypePtr, ident,
tagSpace, defined); tagSpace, defined);
structTypePtr^.sName := structPtr^.name; structTypePtr^.sName := structPtr^.name;
declaredTagOrEnumConst := true;
end; end;
end {if} end {if}
{the name has been defined, so...} {the name has been defined, so...}
else if structPtr^.itype^.kind <> tKind then begin else if structPtr^.itype^.kind <> tKind then begin
Error(42); {it's an error if it's not a struct} Error(42); {it's an error if it's not a struct}
declaredTagOrEnumConst := true; {avoid extra errors}
structPtr := nil; structPtr := nil;
end {else} end {else}
else begin {record the existing structure type} else begin {record the existing structure type}
structTypePtr := structPtr^.itype; structTypePtr := structPtr^.itype;
end; {else} end; {else}
end {if} end {if}
else if token.kind <> lbracech then else if token.kind <> lbracech then begin
Error(9); {its an error if there's no name or struct} Error(9); {its an error if there's no name or struct}
declaredTagOrEnumConst := true; {avoid extra errors}
end; {else if}
2: if token.kind = lbracech then {handle a structure definition...} 2: if token.kind = lbracech then {handle a structure definition...}
begin {error if we already have one!} begin {error if we already have one!}
if (structTypePtr <> defaultStruct) if (structTypePtr <> defaultStruct)
@ -3651,12 +3670,17 @@ inhibitHeader := true; {block imbedded includes in headers}
lUseGlobalPool := useGlobalPool; lUseGlobalPool := useGlobalPool;
{handle a TypeSpecifier/declarator} {handle a TypeSpecifier/declarator}
typeFound := token.kind in declarationSpecifiersElement; typeFound := token.kind in declarationSpecifiersElement;
declaredTagOrEnumConst := false;
DeclarationSpecifiers(declSpecifiers, declarationSpecifiersElement, ident); DeclarationSpecifiers(declSpecifiers, declarationSpecifiersElement, ident);
isPascal := pascalsy in declSpecifiers.declarationModifiers; isPascal := pascalsy in declSpecifiers.declarationModifiers;
isAsm := asmsy in declSpecifiers.declarationModifiers; isAsm := asmsy in declSpecifiers.declarationModifiers;
isInline := inlinesy in declSpecifiers.declarationModifiers; isInline := inlinesy in declSpecifiers.declarationModifiers;
isNoreturn := _Noreturnsy in declSpecifiers.declarationModifiers; isNoreturn := _Noreturnsy in declSpecifiers.declarationModifiers;
alignmentSpecified := _Alignassy in declSpecifiers.declarationModifiers; alignmentSpecified := _Alignassy in declSpecifiers.declarationModifiers;
if token.kind = semicolonch then
if not doingPrototypes then
if not declaredTagOrEnumConst then
Error(176);
if not skipDeclarator then begin if not skipDeclarator then begin
variable := nil; variable := nil;
Declarator(declSpecifiers, variable, variableSpace, doingPrototypes); Declarator(declSpecifiers, variable, variableSpace, doingPrototypes);

View File

@ -759,6 +759,7 @@ if list or (numErr <> 0) then begin
173: msg := @'''#'' must be followed by a macro parameter'; 173: msg := @'''#'' must be followed by a macro parameter';
174: msg := @'''__VA_ARGS__'' may only be used in a variadic macro'; 174: msg := @'''__VA_ARGS__'' may only be used in a variadic macro';
175: msg := @'duplicate macro parameter name'; 175: msg := @'duplicate macro parameter name';
176: msg := @'declarator expected';
otherwise: Error(57); otherwise: Error(57);
end; {case} end; {case}
writeln(msg^); writeln(msg^);