diff --git a/clang/Parse/ParseDecl.cpp b/clang/Parse/ParseDecl.cpp index 3fc1ed6ff5e3..99f0dcf386f2 100644 --- a/clang/Parse/ParseDecl.cpp +++ b/clang/Parse/ParseDecl.cpp @@ -160,6 +160,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) { //case tok::kw_union: //case tok::kw_enum: + //case tok::identifier: + // TODO: handle typedef names. + // type-qualifier case tok::kw_const: isInvalid = DS.SetTypeQual(DeclSpec::TQ_const , PrevSpec, getLang())*2; @@ -189,6 +192,56 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) { } } +/// isDeclarationSpecifier() - Return true if the current token is part of a +/// declaration specifier. +bool Parser::isDeclarationSpecifier() const { + switch (Tok.getKind()) { + default: return false; + // storage-class-specifier + case tok::kw_typedef: + case tok::kw_extern: + case tok::kw_static: + case tok::kw_auto: + case tok::kw_register: + case tok::kw___thread: + + // type-specifiers + case tok::kw_short: + case tok::kw_long: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw__Complex: + case tok::kw__Imaginary: + case tok::kw_void: + case tok::kw_char: + case tok::kw_int: + case tok::kw_float: + case tok::kw_double: + case tok::kw__Bool: + case tok::kw__Decimal32: + case tok::kw__Decimal64: + case tok::kw__Decimal128: + + // struct-or-union-specifier + case tok::kw_struct: + case tok::kw_union: + // enum-specifier + case tok::kw_enum: + // type-qualifier + case tok::kw_const: + case tok::kw_volatile: + case tok::kw_restrict: + // function-specifier + case tok::kw_inline: + return true; + // typedef-name + case tok::identifier: + // FIXME: if this is a typedef return true. + return false; + // TODO: Attributes. + } +} + /// ParseDeclarator /// declarator: [C99 6.7.5] @@ -268,6 +321,55 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS) { /// [GNU] direct-declarator '(' parameter-forward-declarations /// parameter-type-list[opt] ')' /// +void Parser::ParseDirectDeclarator(Declarator &D) { + // Parse the first direct-declarator seen. + if (Tok.getKind() == tok::identifier && D.mayHaveIdentifier()) { + assert(Tok.getIdentifierInfo() && "Not an identifier?"); + D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); + ConsumeToken(); + } else if (Tok.getKind() == tok::l_paren) { + // direct-declarator: '(' declarator ')' + // direct-declarator: '(' attributes declarator ')' [TODO] + // Example: 'char (*X)' or 'int (*XX)(void)' + ParseParenDeclarator(D); + } else if (Tok.getKind() == tok::l_square && + D.mayOmitIdentifier()) { + // direct-abstract-declarator[opt] '[' assignment-expression[opt] ']' + // direct-abstract-declarator[opt] '[' '*' ']' + + // direct-abstract-declarator was not specified. Remember that this is the + // place where the identifier would have been. + D.SetIdentifier(0, Tok.getLocation()); + // Don't consume the '[', handle it below. + } else if (D.mayOmitIdentifier()) { + // This could be something simple like "int" (in which case the declarator + // portion is empty), if an abstract-declarator is allowed. + D.SetIdentifier(0, Tok.getLocation()); + } else { + // expected identifier or '(' or '['. + assert(0 && "ERROR: should recover!"); + } + + assert(D.isPastIdentifier() && + "Haven't past the location of the identifier yet?"); + + while (1) { + if (Tok.getKind() == tok::l_paren) { + ParseParenDeclarator(D); + } else if (Tok.getKind() == tok::l_square) { + assert(0 && "Unimp!"); + } else { + break; + } + } +} + +/// ParseParenDeclarator - We parsed the declarator D up to a paren. This may +/// either be before the identifier (in which case these are just grouping +/// parens for precedence) or it may be after the identifier, in which case +/// these are function arguments. +/// +/// This method also handles this portion of the grammar: /// parameter-type-list: [C99 6.7.5] /// parameter-list /// parameter-list ',' '...' @@ -278,33 +380,150 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS) { /// /// parameter-declaration: [C99 6.7.5] /// declaration-specifiers declarator -/// [GNU] declaration-specifiers declarator attributes +/// [GNU] declaration-specifiers declarator attributes [TODO] /// declaration-specifiers abstract-declarator[opt] -/// [GNU] declaration-specifiers abstract-declarator[opt] attributes +/// [GNU] declaration-specifiers abstract-declarator[opt] attributes [TODO] /// /// identifier-list: [C99 6.7.5] /// identifier /// identifier-list ',' identifier /// -void Parser::ParseDirectDeclarator(Declarator &D) { - // Parse the first direct-declarator seen. - if (Tok.getKind() == tok::identifier) { - assert(Tok.getIdentifierInfo() && "Not an identifier?"); - D.SetIdentifier(Tok.getIdentifierInfo()); - ConsumeToken(); - } else if (0 && Tok.getKind() == tok::l_paren) { - //char (*X); - //int (*XX)(void); - } +void Parser::ParseParenDeclarator(Declarator &D) { + ConsumeParen(); - while (1) { - if (Tok.getKind() == tok::l_paren) { - assert(0 && "Unimp!"); - } else if (Tok.getKind() == tok::l_square) { - assert(0 && "Unimp!"); + // If we haven't past the identifier yet (or where the identifier would be + // stored, if this is an abstract declarator), then this is probably just + // grouping parens. + if (!D.isPastIdentifier()) { + // Okay, this is probably a grouping paren. However, if this could be an + // abstract-declarator, then this could also be the start of function + // arguments (consider 'void()'). + bool isGrouping; + + if (!D.mayOmitIdentifier()) { + // If this can't be an abstract-declarator, this *must* be a grouping + // paren, because we haven't seen the identifier yet. + isGrouping = true; + } else if (Tok.getKind() == tok::r_paren || // 'int()' is a function. + isDeclarationSpecifier()) { // 'int(int)' is a function. + + isGrouping = false; } else { - break; + // Otherwise, 'int (*X)', this is a grouping paren. + isGrouping = true; } + + // If this is a grouping paren, handle: + // direct-declarator: '(' declarator ')' + // direct-declarator: '(' attributes declarator ')' [TODO] + if (isGrouping) { + ParseDeclarator(D); + // expected ')': skip until we find ')'. + if (Tok.getKind() != tok::r_paren) + assert(0 && "Recover!"); + ConsumeParen(); + return; + } + + // Okay, if this wasn't a grouping paren, it must be the start of a function + // argument list. Recognize that this will never have an identifier (and + // where it would be), then fall through to the handling of argument lists. + D.SetIdentifier(0, Tok.getLocation()); } + // Okay, this is the parameter list of a function definition, or it is an + // identifier list of a K&R-style function. + + // FIXME: enter function-declaration scope, limiting any declarators for + // arguments to the function scope. + // NOTE: better to only create a scope if not '()' + bool isVariadic; + bool HasPrototype; + if (Tok.getKind() == tok::r_paren) { + // int() -> no prototype, no '...'. + isVariadic = false; + HasPrototype = false; + } else if (Tok.getKind() == tok::identifier && + 0/*TODO: !isatypedefname(Tok.getIdentifierInfo())*/) { + // Identifier list. Note that '(' identifier-list ')' is only allowed for + // normal declarators, not for abstract-declarators. + assert(D.isPastIdentifier() && "Identifier (if present) must be passed!"); + + // If there was no identifier specified, either we are in an + // abstract-declarator, or we are in a parameter declarator which was found + // to be abstract. In abstract-declarators, identifier lists are not valid, + // diagnose this. + if (!D.getIdentifier()) + Diag(Tok, diag::ext_ident_list_in_param); + + // FIXME: Remember token. + ConsumeToken(); + while (Tok.getKind() == tok::comma) { + // Eat the comma. + ConsumeToken(); + + // FIXME: if not identifier, consume until ')' then break. + assert(Tok.getKind() == tok::identifier); + + // Eat the id. + // FIXME: remember it! + ConsumeToken(); + } + + // FIXME: if not identifier, consume until ')' then break. + assert(Tok.getKind() == tok::r_paren); + + // K&R 'prototype'. + isVariadic = false; + HasPrototype = false; + } else { + isVariadic = false; + bool ReadArg = false; + // Finally, a normal, non-empty parameter type list. + while (1) { + if (Tok.getKind() == tok::ellipsis) { + isVariadic = true; + + // Check to see if this is "void(...)" which is not allowed. + if (!ReadArg) { + // Otherwise, parse parameter type list. If it starts with an ellipsis, + // diagnose the malformed function. + Diag(Tok, diag::err_ellipsis_first_arg); + isVariadic = false; // Treat this like 'void()'. + } + + // Consume the ellipsis. + ConsumeToken(); + break; + } + + ReadArg = true; + + // Parse the declaration-specifiers. + DeclSpec DS; + ParseDeclarationSpecifiers(DS); + + // Parse the declarator. This is "PrototypeContext", because we must + // accept either 'declarator' or 'abstract-declarator' here. + Declarator DeclaratorInfo(DS, Declarator::PrototypeContext); + ParseDeclarator(DeclaratorInfo); + + // TODO: do something with the declarator, if it is valid. + + // If the next token is a comma, consume it and keep reading arguments. + if (Tok.getKind() != tok::comma) break; + + // Consume the comma. + ConsumeToken(); + } + + HasPrototype = true; + } + + + // expected ')': skip until we find ')'. + if (Tok.getKind() != tok::r_paren) + assert(0 && "Recover!"); + ConsumeParen(); } + diff --git a/clang/Parse/Parser.cpp b/clang/Parse/Parser.cpp index e8e3f997c422..8e73ee005fd8 100644 --- a/clang/Parse/Parser.cpp +++ b/clang/Parse/Parser.cpp @@ -21,6 +21,7 @@ Parser::Parser(Preprocessor &pp, ParserActions &actions) : PP(pp), Actions(actions), Diags(PP.getDiagnostics()) { // Create the global scope, install it as the current scope. CurScope = new Scope(0); + Tok.SetKind(tok::eof); } Parser::~Parser() { @@ -109,7 +110,7 @@ void Parser::ParseDeclarationOrFunctionDefinition() { // Parse the declarator. { - Declarator DeclaratorInfo(DS); + Declarator DeclaratorInfo(DS, Declarator::FileContext); ParseDeclarator(DeclaratorInfo); // If the declarator was a function type... handle it. @@ -125,7 +126,7 @@ void Parser::ParseDeclarationOrFunctionDefinition() { ConsumeToken(); // Parse the declarator. - Declarator DeclaratorInfo(DS); + Declarator DeclaratorInfo(DS, Declarator::FileContext); ParseDeclarator(DeclaratorInfo); // declarator '=' initializer diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index 3045f78e7628..dbae415928ff 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -250,6 +250,9 @@ DIAG(ext_integer_complex, EXTENSION, DIAG(ext_thread_before, EXTENSION, "'__thread' before 'static'") +DIAG(ext_ident_list_in_param, EXTENSION, + "type-less parameter names in function declaration") + DIAG(err_parse_error, ERROR, "parse error") DIAG(err_invalid_decl_spec_combination, ERROR, @@ -266,5 +269,7 @@ DIAG(err_invalid_complex_spec, ERROR, "'_Complex %s' is invalid") DIAG(err_invalid_thread_spec, ERROR, "'__thread %s' is invalid") +DIAG(err_ellipsis_first_arg, ERROR, + "ISO C requires a named argument before '...'") #undef DIAG diff --git a/clang/include/clang/Parse/DeclSpec.h b/clang/include/clang/Parse/DeclSpec.h index 93636d97b1dc..e511c1e8b3a9 100644 --- a/clang/include/clang/Parse/DeclSpec.h +++ b/clang/include/clang/Parse/DeclSpec.h @@ -15,11 +15,11 @@ #define LLVM_CLANG_PARSE_DECLARATIONS_H #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceLocation.h" namespace llvm { namespace clang { class LangOptions; - class SourceLocation; class IdentifierInfo; /// DeclSpec - This class captures information about "declaration specifiers", @@ -134,16 +134,58 @@ public: /// DeclaratorInfo - Information about one declarator, including the parsed type /// information and the identifier. When the declarator is fully formed, this /// is turned into the appropriate Decl object. +/// +/// Declarators come in two types: normal declarators and abstract declarators. +/// Abstract declarators are used when parsing types, and don't have an +/// identifier. Normal declarators do have ID's. One strange case s class Declarator { const DeclSpec &DS; IdentifierInfo *Identifier; + SourceLocation IdentifierLoc; + public: - Declarator(const DeclSpec &ds) : DS(ds) { - Identifier = 0; + enum TheContext { + FileContext, // File scope declaration. + PrototypeContext, // Within a function prototype. + KNRTypeListContext, // K&R type definition list for formals. + TypeNameContext, // Abstract declarator for types. + MemberContext, // Struct/Union field. + BlockContext, // Declaration within a block in a function. + ForContext // Declaration within first part of a for loop. + }; +private: + /// Context - Where we are parsing this declarator. + /// + TheContext Context; +public: + Declarator(const DeclSpec &ds, TheContext C) + : DS(ds), Identifier(0), Context(C) { } + /// mayOmitIdentifier - Return true if the identifier is either optional or + /// not allowed. This is true for typenames and prototypes. + bool mayOmitIdentifier() const { + return Context == TypeNameContext || Context == PrototypeContext; + } + + /// mayHaveIdentifier - Return true if the identifier is either optional or + /// required. This is true for normal declarators and prototypes, but not + /// typenames. + bool mayHaveIdentifier() const { + return Context != TypeNameContext; + } + + /// isPastIdentifier - Return true if we have parsed beyond the point where + /// the + bool isPastIdentifier() const { return IdentifierLoc.isValid(); } + IdentifierInfo *getIdentifier() const { return Identifier; } - void SetIdentifier(IdentifierInfo *ID) { Identifier = ID; } + SourceLocation getIdentifierLoc() const { return IdentifierLoc; } + + void SetIdentifier(IdentifierInfo *ID, SourceLocation Loc) { + Identifier = ID; + IdentifierLoc = Loc; + } }; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 6364b0516100..72660198dbff 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -62,12 +62,24 @@ public: } /// ConsumeToken - Consume the current 'peek token', lexing a new one and - /// returning the token kind. - tok::TokenKind ConsumeToken() { + /// returning the token kind. This does not work will all kinds of tokens, + /// strings and parens must be consumed with custom methods below. + void ConsumeToken() { + assert(Tok.getKind() != tok::string_literal && + Tok.getKind() != tok::l_paren && + Tok.getKind() != tok::r_paren && + "Should consume special tokens with Consume*Token"); PP.Lex(Tok); - return Tok.getKind(); } + /// ConsumeParen - This consume method keeps the paren count up-to-date. + /// + void ConsumeParen() { + assert((Tok.getKind() == tok::l_paren || + Tok.getKind() == tok::r_paren) && "wrong consume method"); + PP.Lex(Tok); + } + private: //===--------------------------------------------------------------------===// // C99 6.9: External Definitions. @@ -78,10 +90,12 @@ private: //===--------------------------------------------------------------------===// // C99 6.7: Declarations. void ParseDeclarationSpecifiers(DeclSpec &DS); + bool isDeclarationSpecifier() const; void ParseDeclarator(Declarator &D); void ParseTypeQualifierListOpt(DeclSpec &DS); void ParseDirectDeclarator(Declarator &D); + void ParseParenDeclarator(Declarator &D); };