diff options
-rw-r--r-- | include/clang/Basic/DiagnosticParseKinds.td | 3 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 75 | ||||
-rw-r--r-- | test/Lexer/block_cmt_end.c | 2 | ||||
-rw-r--r-- | test/Parser/declarators.c | 19 | ||||
-rw-r--r-- | test/Parser/objc-foreach-syntax.m | 10 | ||||
-rw-r--r-- | test/SemaObjC/exception-go-boom.m | 2 |
6 files changed, 98 insertions, 13 deletions
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index c2e90e8da4..66a48cffb0 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -143,6 +143,9 @@ def err_typename_invalid_functionspec : Error< "type name does not allow function specifier to be specified">; def err_invalid_decl_spec_combination : Error< "cannot combine with previous '%0' declaration specifier">; +def err_unknown_typename : Error< + "unknown type name %0">; + /// Objective-C parser diagnostics def err_objc_no_attributes_on_category : Error< diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 83c02490fe..76bdd2f487 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -467,6 +467,29 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS) { } } +/// isValidAfterIdentifierInDeclaratorAfterDeclSpec - Return true if the +/// specified token is valid after the identifier in a declarator which +/// immediately follows the declspec. For example, these things are valid: +/// +/// int x [ 4]; // direct-declarator +/// int x ( int y); // direct-declarator +/// int(int x ) // direct-declarator +/// int x ; // simple-declaration +/// int x = 17; // init-declarator-list +/// int x , y; // init-declarator-list +/// int x __asm__ ("foo"); // init-declarator-list +/// +/// This is not, because 'x' does not immediately follow the declspec (though +/// ')' happens to be valid anyway). +/// int (x) +/// +static bool isValidAfterIdentifierInDeclarator(const Token &T) { + return T.is(tok::l_square) || T.is(tok::l_paren) || T.is(tok::r_paren) || + T.is(tok::semi) || T.is(tok::comma) || T.is(tok::equal) || + T.is(tok::kw_asm); + +} + /// ParseDeclarationSpecifiers /// declaration-specifiers: [C99 6.7] /// storage-class-specifier declaration-specifiers[opt] @@ -489,7 +512,7 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS) { /// void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, TemplateParameterLists *TemplateParams, - AccessSpecifier AS){ + AccessSpecifier AS) { DS.SetRangeStart(Tok.getLocation()); while (1) { int isInvalid = false; @@ -604,15 +627,59 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, TypeTy *TypeRep = Actions.getTypeName(*Tok.getIdentifierInfo(), Tok.getLocation(), CurScope); - if (TypeRep == 0) + // If this is not a typedef name, don't parse it as part of the declspec, + // it must be an implicit int or an error. + if (TypeRep == 0) { + // If we see an identifier that is not a type name, we normally would + // parse it as the identifer being declared. However, when a typename + // is typo'd or the definition is not included, this will incorrectly + // parse the typename as the identifier name and fall over misparsing + // later parts of the diagnostic. + // + // As such, we try to do some look-ahead in cases where this would + // otherwise be an "implicit-int" case to see if this is invalid. For + // example: "static foo_t x = 4;" In this case, if we parsed foo_t as + // an identifier with implicit int, we'd get a parse error because the + // next token is obviously invalid for a type. Parse these as a case + // with an invalid type specifier. + assert(!DS.hasTypeSpecifier() && "Type specifier checked above"); + + // Since we know that this either implicit int (which is rare) or an + // error, we'd do lookahead to try to do better recovery. + if (isValidAfterIdentifierInDeclarator(NextToken())) { + // If this token is valid for implicit int, e.g. "static x = 4", then + // we just avoid eating the identifier, so it will be parsed as the + // identifier in the declarator. + goto DoneWithDeclSpec; + } + + // Otherwise, if we don't consume this token, we are going to emit an + // error anyway. Since this is almost certainly an invalid type name, + // emit a diagnostic that says it, eat the token, and pretend we saw an + // 'int'. + Diag(Loc, diag::err_unknown_typename) << Tok.getIdentifierInfo(); + DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec); + DS.SetRangeEnd(Tok.getLocation()); + ConsumeToken(); + + // TODO: in C, we could redo the lookup in the tag namespace to catch + // things like "foo x" where the user meant "struct foo x" etc, this + // would be much nicer for both error recovery, diagnostics, and we + // could even emit a fixit hint. + + // TODO: Could inject an invalid typedef decl in an enclosing scope to + // avoid rippling error messages on subsequent uses of the same type, + // could be useful if #include was forgotten. + + // FIXME: Mark DeclSpec as invalid. goto DoneWithDeclSpec; + } // C++: If the identifier is actually the name of the class type // being defined and the next token is a '(', then this is a // constructor declaration. We're done with the decl-specifiers // and will treat this token as an identifier. - if (getLang().CPlusPlus && - CurScope->isClassScope() && + if (getLang().CPlusPlus && CurScope->isClassScope() && Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope) && NextToken().getKind() == tok::l_paren) goto DoneWithDeclSpec; diff --git a/test/Lexer/block_cmt_end.c b/test/Lexer/block_cmt_end.c index 65d948f7fd..d85cf81f21 100644 --- a/test/Lexer/block_cmt_end.c +++ b/test/Lexer/block_cmt_end.c @@ -17,7 +17,7 @@ next comment ends with normal escaped newline: /* expected-warning {{escaped newline}} expected-warning {{backslash and newline}} *\ / -bar +int bar /* xyz diff --git a/test/Parser/declarators.c b/test/Parser/declarators.c index 8a533ee53d..09b43e5057 100644 --- a/test/Parser/declarators.c +++ b/test/Parser/declarators.c @@ -12,7 +12,7 @@ char ((((*X)))); void (*signal(int, void (*)(int)))(int); -int a, ***C, * const D, b(int); +int a, ***C, * const D, B(int); int *A; @@ -36,3 +36,20 @@ int test4(x, x) int x; {} /* expected-error {{redefinition of parameter 'x'}} */ // PR3031 int (test5), ; // expected-error {{expected identifier or '('}} + + +// PR3963 & rdar://6759604 - test error recovery for mistyped "typenames". + +struct xyz { int y; }; + +foo_t a = 4; // expected-error {{unknown type name 'foo_t'}} +xyz b; // expected-error {{unknown type name 'xyz'}} + +foo_t *d; // expected-error {{unknown type name 'foo_t'}} + +static f; // expected-warning {{type specifier missing, defaults to 'int'}} +static g = 4; // expected-warning {{type specifier missing, defaults to 'int'}} +static h // expected-warning {{type specifier missing, defaults to 'int'}} + __asm__("foo"); // expected-warning {{extension used}} + +int bar() { return a; } diff --git a/test/Parser/objc-foreach-syntax.m b/test/Parser/objc-foreach-syntax.m index e6e3ccf12d..977dccc88b 100644 --- a/test/Parser/objc-foreach-syntax.m +++ b/test/Parser/objc-foreach-syntax.m @@ -1,10 +1,8 @@ // RUN: clang-cc -fsyntax-only -verify %s -ce MyList // expected-error {{invalid token after top level declarator}} -@end -@implementation MyList +@implementation MyList // expected-warning {{cannot find interface declaration for 'MyList'}} - (unsigned int)countByEnumeratingWithState: (struct __objcFastEnumerationState *)state objects: (id *)items count:(unsigned int)stackcount { return 0; @@ -14,10 +12,10 @@ ce MyList // expected-error {{invalid token after top level declarator}} int LOOP(); -@implementation MyList (BasicTest) // expected-error {{cannot find interface declaration for 'MyList'}} +@implementation MyList (BasicTest) - (void)compilerTestAgainst { -MyList * el; // expected-error {{use of undeclared identifier 'MyList'}} - for (el in @"foo") // expected-error {{use of undeclared identifier 'el'}} +MyList * el; + for (el in @"foo") { LOOP(); } } @end diff --git a/test/SemaObjC/exception-go-boom.m b/test/SemaObjC/exception-go-boom.m index 1d792c4c13..774ae7cd63 100644 --- a/test/SemaObjC/exception-go-boom.m +++ b/test/SemaObjC/exception-go-boom.m @@ -4,7 +4,7 @@ void f0(id x) { @try { } @catch (NSException *x) { // \ - expected-warning{{type specifier missing, defaults to 'int'}} \ + expected-error{{unknown type name 'NSException'}} \ expected-error{{@catch parameter is not a pointer to an interface type}} } } |