Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -1015,6 +1015,8 @@ "expected a module name after module import">; def err_module_expected_semi : Error< "expected ';' after module name">; +def err_unexpected_module_end : Error<"unexpected module end">; +def err_unexpected_module_start : Error<"submodule cannot be started here">; } let CategoryName = "Generics Issue" in { Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -2554,6 +2554,7 @@ //===--------------------------------------------------------------------===// // Modules DeclGroupPtrTy ParseModuleImport(SourceLocation AtLoc); + bool tryParseMisplacedModuleImport(); //===--------------------------------------------------------------------===// // C++11/G++: Type Traits [Type-Traits.html in the GCC manual] Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1778,6 +1778,10 @@ /// \brief The parser has left a submodule. void ActOnModuleEnd(SourceLocation DirectiveLoc, Module *Mod); + /// \brief Check if module import may be found in the current context, + /// emit error if not. + void diagnoseMisplacedModuleImport(Module *M, SourceLocation ImportLoc); + /// \brief Create an implicit import of the given module at the given /// source location, for error recovery, if possible. /// Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -210,7 +210,13 @@ ParsedAttributes &attrs, BalancedDelimiterTracker &Tracker) { if (index == Ident.size()) { - while (Tok.isNot(tok::r_brace) && !isEofOrEom()) { + while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + if (Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin, + tok::annot_module_end)) { + if (tryParseMisplacedModuleImport()) + continue; + break; + } ParsedAttributesWithRange attrs(AttrFactory); MaybeParseCXX11Attributes(attrs); MaybeParseMicrosoftAttributes(attrs); @@ -3063,11 +3069,17 @@ if (TagDecl) { // While we still have something to read, read the member-declarations. - while (Tok.isNot(tok::r_brace) && !isEofOrEom()) + while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + if (Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin, + tok::annot_module_end)) { + if (tryParseMisplacedModuleImport()) + continue; + break; + } // Each iteration of this loop reads one member-declaration. ParseCXXClassMemberDeclarationWithPragmas( CurAS, AccessAttrs, static_cast(TagType), TagDecl); - + } T.consumeClose(); } else { SkipUntil(tok::r_brace); Index: lib/Parse/ParseStmt.cpp =================================================================== --- lib/Parse/ParseStmt.cpp +++ lib/Parse/ParseStmt.cpp @@ -944,7 +944,13 @@ Stmts.push_back(R.get()); } - while (Tok.isNot(tok::r_brace) && !isEofOrEom()) { + while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + if (Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin, + tok::annot_module_end)) { + if (tryParseMisplacedModuleImport()) + continue; + break; + } if (Tok.is(tok::annot_pragma_unused)) { HandlePragmaUnused(); continue; Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -1989,6 +1989,43 @@ return Actions.ConvertDeclToDeclGroup(Import.get()); } +/// \brief Try recover parser when module annotation appears where it must not +/// be found. +/// \returns true if the recover was successful and parsing may be continued, or +/// false if parser must bail out to top level and handle the token there. +bool Parser::tryParseMisplacedModuleImport() { + switch (Tok.getKind()) { + case tok::annot_module_end: + Diag(Tok, diag::err_unexpected_module_end); + break; + case tok::annot_module_begin: { + Diag(Tok, diag::err_unexpected_module_start); + // Recover by skipping content of the included submodule. + unsigned ModuleNesting = 1; + do { + ConsumeAnyToken(); + if (SkipUntil(tok::annot_module_end)) { + --ModuleNesting; + } else { + if (Tok.is(tok::annot_module_begin)) + ++ModuleNesting; + } + } while (ModuleNesting && Tok.isNot(tok::eof)); + return true; + } + case tok::annot_module_include: + // Module import found where it should not be, for instance, inside a + // namespace. Recover by ignoring the import. + Actions.diagnoseMisplacedModuleImport(reinterpret_cast( + Tok.getAnnotationValue()), Tok.getLocation()); + ConsumeToken(); + return true; + default: + llvm_unreachable("Module annotation token expected"); + } + return false; +} + bool BalancedDelimiterTracker::diagnoseOverflow() { P.Diag(P.Tok, diag::err_bracket_depth_exceeded) << P.getLangOpts().BracketDepth; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -14274,6 +14274,10 @@ } } +void Sema::diagnoseMisplacedModuleImport(Module *M, SourceLocation ImportLoc) { + return checkModuleImportContext(*this, M, ImportLoc, CurContext); +} + DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, SourceLocation ImportLoc, ModuleIdPath Path) { Index: test/Modules/auto-module-import.m =================================================================== --- test/Modules/auto-module-import.m +++ test/Modules/auto-module-import.m @@ -83,6 +83,6 @@ return not_in_module; } -void includeNotAtTopLevel() { // expected-note {{to match this '{'}} - #include // expected-warning {{treating #include as an import}} expected-error {{expected '}'}} -} // expected-error {{extraneous closing brace}} +void includeNotAtTopLevel() { // expected-note {{function 'includeNotAtTopLevel' begins here}} + #include // expected-warning {{treating #include as an import}} expected-error {{import of module 'NoUmbrella.A' appears within function 'includeNotAtTopLevel'}} +} Index: test/Modules/malformed.cpp =================================================================== --- test/Modules/malformed.cpp +++ test/Modules/malformed.cpp @@ -12,20 +12,14 @@ #include STR(HEADER) // CHECK-A: While building module 'malformed_a' +// CHECK-A: {{^}}Inputs/malformed/a1.h:1:{{.*}} error: unexpected module end // CHECK-A: {{^}}Inputs/malformed/a1.h:1:{{.*}} error: expected '}' // CHECK-A: {{^}}Inputs/malformed/a1.h:1:{{.*}} note: to match this '{' // // CHECK-A: While building module 'malformed_a' -// CHECK-A: {{^}}Inputs/malformed/a2.h:1:{{.*}} error: extraneous closing brace // CHECK-B: While building module 'malformed_b' -// CHECK-B: {{^}}Inputs/malformed/b1.h:2:{{.*}} error: expected '}' -// CHECK-B: {{^}}Inputs/malformed/b1.h:1:{{.*}} note: to match this '{' -// CHECK-B: {{^}}Inputs/malformed/b1.h:3:{{.*}} error: extraneous closing brace ('}') -// -// CHECK-B: While building module 'malformed_b' -// CHECK-B: {{^}}Inputs/malformed/b2.h:1:{{.*}} error: redefinition of 'g' -// CHECK-B: {{^}}Inputs/malformed/b2.h:1:{{.*}} note: previous definition is here +// CHECK-B: {{^}}Inputs/malformed/b1.h:2:{{.*}} error: submodule cannot be started here void test() { f(); } // Test that we use relative paths to name files within an imported module. Index: test/Modules/misplaced.cpp =================================================================== --- /dev/null +++ test/Modules/misplaced.cpp @@ -0,0 +1,14 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs %s -verify + +namespace N1 { // expected-note{{namespace 'N1' begins here}} +#include "dummy.h" // expected-error{{import of module 'dummy' appears within namespace 'N1'}} +} + +void func1() { // expected-note{{function 'func1' begins here}} +#include "dummy.h" // expected-error{{import of module 'dummy' appears within function 'func1'}} +} + +struct S1 { // expected-note{{'S1' begins here}} +#include "dummy.h" // expected-error{{import of module 'dummy' appears within 'S1'}} +};