diff options
-rw-r--r-- | include/clang/Parse/Parser.h | 15 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 26 | ||||
-rw-r--r-- | lib/Parse/Parser.cpp | 2 | ||||
-rw-r--r-- | test/Parser/objc-recover.mm | 64 |
4 files changed, 100 insertions, 7 deletions
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index b9414a4051..14ae6604b2 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -23,6 +23,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/SaveAndRestore.h" #include <stack> namespace clang { @@ -83,6 +84,7 @@ class Parser : public CodeCompletionHandler { friend class ColonProtectionRAIIObject; friend class InMessageExpressionRAIIObject; friend class PoisonSEHIdentifiersRAIIObject; + friend class ObjCDeclContextSwitch; friend class ParenBraceBracketBalancer; friend class BalancedDelimiterTracker; @@ -203,6 +205,13 @@ class Parser : public CodeCompletionHandler { IdentifierInfo *getSEHExceptKeyword(); + /// True if we are within an Objective-C container while parsing C-like decls. + /// + /// This is necessary because Sema thinks we have left the container + /// to parse the C-like decls, meaning Actions.getObjCDeclContext() will + /// be NULL. + bool ParsingInObjCContainer; + bool SkipFunctionBodies; public: @@ -556,9 +565,11 @@ private: class ObjCDeclContextSwitch { Parser &P; Decl *DC; + SaveAndRestore<bool> WithinObjCContainer; public: - explicit ObjCDeclContextSwitch(Parser &p) : P(p), - DC(p.getObjCDeclContext()) { + explicit ObjCDeclContextSwitch(Parser &p) + : P(p), DC(p.getObjCDeclContext()), + WithinObjCContainer(P.ParsingInObjCContainer, DC != 0) { if (DC) P.Actions.ActOnObjCTemporaryExitContainerContext(cast<DeclContext>(DC)); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index d91457c0d6..05d44a5af0 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1270,15 +1270,33 @@ void Parser::SkipMalformedDecl() { case tok::kw_inline: // 'inline namespace' at the start of a line is almost certainly - // a good place to pick back up parsing. - if (Tok.isAtStartOfLine() && NextToken().is(tok::kw_namespace)) + // a good place to pick back up parsing, except in an Objective-C + // @interface context. + if (Tok.isAtStartOfLine() && NextToken().is(tok::kw_namespace) && + (!ParsingInObjCContainer || CurParsedObjCImpl)) return; break; case tok::kw_namespace: // 'namespace' at the start of a line is almost certainly a good - // place to pick back up parsing. - if (Tok.isAtStartOfLine()) + // place to pick back up parsing, except in an Objective-C + // @interface context. + if (Tok.isAtStartOfLine() && + (!ParsingInObjCContainer || CurParsedObjCImpl)) + return; + break; + + case tok::at: + // @end is very much like } in Objective-C contexts. + if (NextToken().isObjCAtKeyword(tok::objc_end) && + ParsingInObjCContainer) + return; + break; + + case tok::minus: + case tok::plus: + // - and + probably start new method declarations in Objective-C contexts. + if (Tok.isAtStartOfLine() && ParsingInObjCContainer) return; break; diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 0703133849..d0c988d1db 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -49,7 +49,7 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool SkipFunctionBodies) : PP(pp), Actions(actions), Diags(PP.getDiagnostics()), GreaterThanIsOperator(true), ColonIsSacred(false), InMessageExpression(false), TemplateParameterDepth(0), - SkipFunctionBodies(SkipFunctionBodies) { + ParsingInObjCContainer(false), SkipFunctionBodies(SkipFunctionBodies) { Tok.setKind(tok::eof); Actions.CurScope = 0; NumCachedScopes = 0; diff --git a/test/Parser/objc-recover.mm b/test/Parser/objc-recover.mm new file mode 100644 index 0000000000..61444c7178 --- /dev/null +++ b/test/Parser/objc-recover.mm @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s + +@interface StopAtAtEnd +// This used to eat the @end +int 123 // expected-error{{expected unqualified-id}} +@end + +@implementation StopAtAtEnd // no-warning +int 123 // expected-error{{expected unqualified-id}} +@end + + +@interface StopAtMethodDecls +// This used to eat the method declarations +int 123 // expected-error{{expected unqualified-id}} +- (void)foo; // expected-note{{here}} +int 456 // expected-error{{expected unqualified-id}} ++ (void)bar; // expected-note{{here}} +@end + +@implementation StopAtMethodDecls +int 123 // expected-error{{expected unqualified-id}} +- (id)foo {} // expected-warning{{conflicting return type}} +int 456 // expected-error{{expected unqualified-id}} ++ (id)bar {} // expected-warning{{conflicting return type}} +@end + + +@interface EmbeddedNamespace +// This used to cause an infinite loop. +namespace NS { // expected-error{{expected unqualified-id}} +} +- (id)test; // expected-note{{here}} +@end + +@implementation EmbeddedNamespace +int 123 // expected-error{{expected unqualified-id}} +// We should still stop here and parse this namespace. +namespace NS { + void foo(); +} + +// Make sure the declaration of -test was recognized. +- (void)test { // expected-warning{{conflicting return type}} + // Make sure the declaration of NS::foo was recognized. + NS::foo(); +} + +@end + + +@protocol ProtocolWithEmbeddedNamespace +namespace NS { // expected-error{{expected unqualified-id}} + +} +- (void)PWEN_foo; // expected-note{{here}} +@end + +@interface ImplementPWEN <ProtocolWithEmbeddedNamespace> +@end + +@implementation ImplementPWEN +- (id)PWEN_foo {} // expected-warning{{conflicting return type}} +@end |