Skip to content

Commit

Permalink
[Modules] More descriptive diagnostics for misplaced import directive
Browse files Browse the repository at this point in the history
If an import directive was put into wrong context, the error message was obscure,
complaining on misbalanced braces. To get more descriptive messages, annotation
tokens related to modules are processed where they must not be seen.

Differential Revision: http://reviews.llvm.org/D11844

llvm-svn: 248085
  • Loading branch information
spavloff committed Sep 19, 2015
1 parent 0510cd5 commit c4e04a2
Show file tree
Hide file tree
Showing 20 changed files with 115 additions and 22 deletions.
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,7 @@ def err_module_expected_ident : Error<
"expected a module name after module import">;
def err_module_expected_semi : Error<
"expected ';' after module name">;
def err_missing_before_module_end : Error<"expected %0 at end of module">;
}

let CategoryName = "Generics Issue" in {
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -7776,6 +7776,8 @@ def note_module_import_in_extern_c : Note<
"extern \"C\" language linkage specification begins here">;
def err_module_import_not_at_top_level : Error<
"import of module '%0' appears within %1">;
def err_module_import_not_at_top_level_fatal : Error<
"import of module '%0' appears within %1">, DefaultFatal;
def note_module_import_not_at_top_level : Note<"%0 begins here">;
def err_module_self_import : Error<
"import of module '%0' appears within same top-level module '%1'">;
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -2555,6 +2555,14 @@ class Parser : public CodeCompletionHandler {
//===--------------------------------------------------------------------===//
// Modules
DeclGroupPtrTy ParseModuleImport(SourceLocation AtLoc);
bool parseMisplacedModuleImport();
bool tryParseMisplacedModuleImport() {
tok::TokenKind Kind = Tok.getKind();
if (Kind == tok::annot_module_begin || Kind == tok::annot_module_end ||
Kind == tok::annot_module_include)
return parseMisplacedModuleImport();
return false;
}

//===--------------------------------------------------------------------===//
// C++11/G++: Type Traits [Type-Traits.html in the GCC manual]
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1799,6 +1799,10 @@ class Sema {
/// \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.
///
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3589,7 +3589,8 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
SmallVector<Decl *, 32> FieldDecls;

// While we still have something to read, read the declarations in the struct.
while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof) &&
!tryParseMisplacedModuleImport()) {
// Each iteration of this loop reads one struct-declaration.

// Check for extraneous top-level semicolon.
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ void Parser::ParseInnerNamespace(std::vector<SourceLocation> &IdentLoc,
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) &&
!tryParseMisplacedModuleImport()) {
ParsedAttributesWithRange attrs(AttrFactory);
MaybeParseCXX11Attributes(attrs);
MaybeParseMicrosoftAttributes(attrs);
Expand Down Expand Up @@ -3063,11 +3064,12 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,

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) &&
!tryParseMisplacedModuleImport()) {
// Each iteration of this loop reads one member-declaration.
ParseCXXClassMemberDeclarationWithPragmas(
CurAS, AccessAttrs, static_cast<DeclSpec::TST>(TagType), TagDecl);

}
T.consumeClose();
} else {
SkipUntil(tok::r_brace);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,8 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
Stmts.push_back(R.get());
}

while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof) &&
!tryParseMisplacedModuleImport()) {
if (Tok.is(tok::annot_pragma_unused)) {
HandlePragmaUnused();
continue;
Expand Down
36 changes: 35 additions & 1 deletion clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2018,6 +2018,37 @@ Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) {
return Actions.ConvertDeclToDeclGroup(Import.get());
}

/// \brief Try recover parser when module annotation appears where it must not
/// be found.
/// \returns false if the recover was successful and parsing may be continued, or
/// true if parser must bail out to top level and handle the token there.
bool Parser::parseMisplacedModuleImport() {
while (true) {
switch (Tok.getKind()) {
case tok::annot_module_end:
// Inform caller that recovery failed, the error must be handled at upper
// level.
return true;
case tok::annot_module_begin:
Actions.diagnoseMisplacedModuleImport(reinterpret_cast<Module *>(
Tok.getAnnotationValue()), Tok.getLocation());
return true;
case tok::annot_module_include:
// Module import found where it should not be, for instance, inside a
// namespace. Recover by importing the module.
Actions.ActOnModuleInclude(Tok.getLocation(),
reinterpret_cast<Module *>(
Tok.getAnnotationValue()));
ConsumeToken();
// If there is another module import, process it.
continue;
default:
return false;
}
}
return false;
}

bool BalancedDelimiterTracker::diagnoseOverflow() {
P.Diag(P.Tok, diag::err_bracket_depth_exceeded)
<< P.getLangOpts().BracketDepth;
Expand Down Expand Up @@ -2045,7 +2076,10 @@ bool BalancedDelimiterTracker::expectAndConsume(unsigned DiagID,
bool BalancedDelimiterTracker::diagnoseMissingClose() {
assert(!P.Tok.is(Close) && "Should have consumed closing delimiter");

P.Diag(P.Tok, diag::err_expected) << Close;
if (P.Tok.is(tok::annot_module_end))
P.Diag(P.Tok, diag::err_missing_before_module_end) << Close;
else
P.Diag(P.Tok, diag::err_expected) << Close;
P.Diag(LOpen, diag::note_matching) << Kind;

// If we're not already at some kind of closing bracket, skip to our closing
Expand Down
11 changes: 7 additions & 4 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14368,14 +14368,17 @@ static void checkModuleImportContext(Sema &S, Module *M,
while (isa<LinkageSpecDecl>(DC))
DC = DC->getParent();
if (!isa<TranslationUnitDecl>(DC)) {
S.Diag(ImportLoc, diag::err_module_import_not_at_top_level)
<< M->getFullModuleName() << DC;
S.Diag(ImportLoc, diag::err_module_import_not_at_top_level_fatal)
<< M->getFullModuleName() << DC;
S.Diag(cast<Decl>(DC)->getLocStart(),
diag::note_module_import_not_at_top_level)
<< DC;
diag::note_module_import_not_at_top_level) << DC;
}
}

void Sema::diagnoseMisplacedModuleImport(Module *M, SourceLocation ImportLoc) {
return checkModuleImportContext(*this, M, ImportLoc, CurContext);
}

DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc,
SourceLocation ImportLoc,
ModuleIdPath Path) {
Expand Down
5 changes: 5 additions & 0 deletions clang/test/Modules/Inputs/misplaced/misplaced-a.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace A {
namespace B { // expected-note{{namespace 'A::B' begins here}}
#include "misplaced-b.h" // expected-error{{import of module 'Misplaced.Sub_B' appears within namespace 'A::B'}}
}
}
1 change: 1 addition & 0 deletions clang/test/Modules/Inputs/misplaced/misplaced-b.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int a;
8 changes: 8 additions & 0 deletions clang/test/Modules/Inputs/misplaced/misplaced.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Misplaced {
module Sub_A {
header "misplaced-a.h"
}
module Sub_B {
header "misplaced-b.h"
}
}
7 changes: 4 additions & 3 deletions clang/test/Modules/auto-module-import.m
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ int getNotInModule() {
return not_in_module;
}

void includeNotAtTopLevel() { // expected-note {{to match this '{'}}
#include <NoUmbrella/A.h> // expected-warning {{treating #include as an import}} expected-error {{expected '}'}}
} // expected-error {{extraneous closing brace}}
void includeNotAtTopLevel() { // expected-note {{function 'includeNotAtTopLevel' begins here}}
#include <NoUmbrella/A.h> // expected-warning {{treating #include as an import}} \
expected-error {{import of module 'NoUmbrella.A' appears within function 'includeNotAtTopLevel'}}
}
4 changes: 3 additions & 1 deletion clang/test/Modules/extern_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ namespace N {
extern "C" {
#endif
int f;
#if !defined(CXX_HEADER)
#if !defined(CXX_HEADER) && !defined(NAMESPACE)
// expected-error@-2 {{redefinition of 'f' as different kind of symbol}}
// expected-note@c-header.h:1 {{previous}}
#endif
Expand All @@ -78,4 +78,6 @@ namespace N {
}
#endif

#if !defined(NAMESPACE)
suppress_expected_no_diagnostics_error error_here; // expected-error {{}}
#endif
10 changes: 2 additions & 8 deletions clang/test/Modules/malformed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,14 @@
#include STR(HEADER)

// CHECK-A: While building module 'malformed_a'
// CHECK-A: {{^}}Inputs/malformed/a1.h:1:{{.*}} error: expected '}'
// CHECK-A: {{^}}Inputs/malformed/a1.h:1:{{.*}} error: expected '}' at end of module
// 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: import of module 'malformed_b.b2' appears within 'S'

void test() { f<int>(); }
// Test that we use relative paths to name files within an imported module.
Expand Down
6 changes: 6 additions & 0 deletions clang/test/Modules/misplaced-1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// 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'}}
}
6 changes: 6 additions & 0 deletions clang/test/Modules/misplaced-2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs %s -verify

void func1() { // expected-note{{function 'func1' begins here}}
#include "dummy.h" // expected-error{{import of module 'dummy' appears within function 'func1'}}
}
6 changes: 6 additions & 0 deletions clang/test/Modules/misplaced-3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs %s -verify

class C1 { // expected-note{{'C1' begins here}}
#include "dummy.h" // expected-error{{import of module 'dummy' appears within 'C1'}}
}
2 changes: 2 additions & 0 deletions clang/test/Modules/misplaced-4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -fmodules -emit-module -fmodule-name=Misplaced -fmodules-cache-path=%t -x c++ -I %S/Inputs %S/Inputs/misplaced/misplaced.modulemap -verify
6 changes: 6 additions & 0 deletions clang/test/Modules/misplaced-5.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs %s -verify

struct S1 { // expected-note{{'struct S1' begins here}}
#include "dummy.h" // expected-error{{import of module 'dummy' appears within 'struct S1'}}
}

0 comments on commit c4e04a2

Please sign in to comment.