aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Basic/DiagnosticParseKinds.td3
-rw-r--r--lib/Parse/ParseDecl.cpp75
-rw-r--r--test/Lexer/block_cmt_end.c2
-rw-r--r--test/Parser/declarators.c19
-rw-r--r--test/Parser/objc-foreach-syntax.m10
-rw-r--r--test/SemaObjC/exception-go-boom.m2
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}}
}
}