On Sat, May 21, 2011 at 9:15 AM, Douglas Gregor <[email protected]> wrote:
>
> On May 19, 2011, at 4:14 PM, Richard Trieu wrote:
>
>
>
> On Sat, May 7, 2011 at 9:59 AM, Douglas Gregor <[email protected]> wrote:
>
>>
>> On May 6, 2011, at 6:57 PM, Richard Trieu wrote:
>>
>> Moved fix-its from the notes to the error message.
>>
>> On Fri, May 6, 2011 at 2:20 PM, Richard Trieu <[email protected]> wrote:
>>
>>> When improperly using "::" to nest namespaces, give a fix-it to correct
>>> it. Parsing will also recover as if they were properly nested.
>>>
>>> namespace foo::bar {
>>> }
>>>
>>> Will result in a fix-it at "::" to change to " { namespace " and an
>>> additional right bracket at the end.
>>>
>>> Patch attached and available through Code Review:
>>> http://codereview.appspot.com/4452052/
>>>
>>
>> + // Unless we have "namepsace foo::bar {", just complain about a lack
>> of '{'.
>>
>> Typo "namepsace".
>>
>> + if (!Tok.is(tok::coloncolon) || !NextToken().is(tok::identifier)) {
>> + Diag(Tok, Ident ? diag::err_expected_lbrace :
>> + diag::err_expected_ident_lbrace);
>> + return 0;
>> + }
>>
>> + // Otherwise, recover parsing as if the user had typed
>> + // "namespace foo { namespace bar {".
>> + TentativeParsingAction TPA(*this);
>> + SkipUntil(tok::l_brace, /*StopAtSemi*/false);
>> + SkipUntil(tok::r_brace, /*StopAtSemi*/false, /*DontConsume*/true);
>> + Token rBraceToken = Tok;
>> + rBraceToken.setKind(tok::r_brace);
>> + PP.EnterToken(rBraceToken);
>> + TPA.Revert();
>>
>> IMO, this isn't robust enough for a Fix-It, because it isn't actually
>> checking for the presence of the '{' or the '}'. For example, if the
>> "namespace foo::bar" was at the end of the translation unit, it looks like
>> we could end up trying to change the end-of-file token to a right-brace.
>>
>> I think it's fine to use tentative parsing here, but it should only be
>> used to identify where the '{' and '}' tokens are so we know that this could
>> be a well-formed namespace definition. Once we've figured that out, I'd
>> prefer that the code not try to change the token stream. Rather, I think it
>> would be better just to parse the
>>
>> namespace foo::bar {
>> and call the appropriate semantic actions to start the foo namespace,
>> then the bar namespace, and keep track of the results of each of those
>> calls. Then, when we see the closing '}', call the appropriate semantic
>> actions to complete those namespace definitions.
>>
>> Also, this code is assuming that we're parsing something like
>>
>> namespace foo::bar {
>> }
>>
>> but not
>>
>> namespace foo::bar::wibble {
>> }
>>
>> Why not generalize it to an arbitrary-length identifier-and-::
>> definitions? If this were a language feature, we'd want it to support an
>> arbitrary depth.
>>
>> - Doug
>>
>
> Changed how the error recovery works. Tentative parsing is still used to
> locate the closing '}', but the token stream is no longer altered. Instead
> of an error for every nested namespace, only produce one error overall with
> the fix-its. Error message will also trigger, but without the fix-its, if
> the namespace is not properly formed. Arbitrary-length nested-namespaces
> are now handled.
>
>
> Great! Only two comments, then I think this patch can go in:
>
> + std::string NamespaceFix = "";
> + for (std::vector<IdentifierInfo*>::iterator I = ExtraIdent.begin(),
> + E = ExtraIdent.end(); I != E; ++I) {
> + NamespaceFix += " { namespace ";
> + NamespaceFix += (*I)->getName();
> + }
> + std::string RBraces(ExtraIdent.size(), '}');
>
> It's just stylistic, but I'd prefer that that RBraces have spaces between
> each '}'.
>
> + // Parse improperly nested namespaces.
> + ParseScope NamespaceScope(this, Scope::DeclScope);
> + Decl *NamespcDecl =
> + Actions.ActOnStartNamespaceDef(getCurScope(), InlineLoc,
> + NamespaceLoc[index], IdentLoc[index],
> + Ident[index], LBrace, attrs.getList());
>
> I think that InlineLoc should only be passed through for the innermost
> namespace, e.g., given
>
> inline namespace foo::bar {
> }
>
> we should pass SourceLocation() for the location of "inline" for "foo", but
> pass InlineLoc for the location of "inline" for "bar".
>
> Thanks for working on this!
>
> - Doug
>
> Okay, I made the two changes you brought up. All look good?
Index: test/Parser/nested-namespaces-recovery.cpp
===================================================================
--- test/Parser/nested-namespaces-recovery.cpp (revision 0)
+++ test/Parser/nested-namespaces-recovery.cpp (revision 0)
@@ -0,0 +1,24 @@
+// RUN: cp %s %t
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: not %clang_cc1 -x c++ -fixit %t
+// RUN: %clang_cc1 -x c++ %t
+
+namespace foo1::foo2::foo3 { // expected-error {{nested namespace definition must define each namespace separately}}
+ int foo(int x) { return x; }
+}
+
+int foo(int x) {
+ return foo1::foo2::foo3::foo(x);
+}
+
+namespace bar1 {
+ namespace bar2 {
+ namespace bar3 {
+ int bar(int x) { return x; }
+ }
+ }
+}
+
+int bar(int x) {
+ return bar1::bar2::bar3::bar(x);
+}
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td (revision 130254)
+++ include/clang/Basic/DiagnosticParseKinds.td (working copy)
@@ -161,6 +161,8 @@
def err_inline_namespace_alias : Error<"namespace alias cannot be inline">;
def err_namespace_nonnamespace_scope : Error<
"namespaces can only be defined in global or namespace scope">;
+def err_nested_namespaces_with_double_colon : Error<
+ "nested namespace definition must define each namespace separately">;
def err_expected_semi_after_attribute_list : Error<
"expected ';' after attribute list">;
def err_expected_semi_after_static_assert : Error<
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h (revision 130254)
+++ include/clang/Parse/Parser.h (working copy)
@@ -1686,6 +1686,12 @@
Decl *ParseNamespace(unsigned Context, SourceLocation &DeclEnd,
SourceLocation InlineLoc = SourceLocation());
+ void ParseInnerNamespace(std::vector<SourceLocation>& IdentLoc,
+ std::vector<IdentifierInfo*>& Ident,
+ std::vector<SourceLocation>& NamespaceLoc,
+ unsigned int index, SourceLocation& InlineLoc,
+ SourceLocation& LBrace, ParsedAttributes& attrs,
+ SourceLocation& RBraceLoc);
Decl *ParseLinkage(ParsingDeclSpec &DS, unsigned Context);
Decl *ParseUsingDirectiveOrDeclaration(unsigned Context,
const ParsedTemplateInfo &TemplateInfo,
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp (revision 130254)
+++ lib/Parse/ParseDeclCXX.cpp (working copy)
@@ -60,12 +60,20 @@
SourceLocation IdentLoc;
IdentifierInfo *Ident = 0;
+ std::vector<SourceLocation> ExtraIdentLoc;
+ std::vector<IdentifierInfo*> ExtraIdent;
+ std::vector<SourceLocation> ExtraNamespaceLoc;
Token attrTok;
if (Tok.is(tok::identifier)) {
Ident = Tok.getIdentifierInfo();
IdentLoc = ConsumeToken(); // eat the identifier.
+ while (Tok.is(tok::coloncolon) && NextToken().is(tok::identifier)) {
+ ExtraNamespaceLoc.push_back(ConsumeToken());
+ ExtraIdent.push_back(Tok.getIdentifierInfo());
+ ExtraIdentLoc.push_back(ConsumeToken());
+ }
}
// Read label attributes, if present.
@@ -85,7 +93,12 @@
return ParseNamespaceAlias(NamespaceLoc, IdentLoc, Ident, DeclEnd);
}
+
if (Tok.isNot(tok::l_brace)) {
+ if (!ExtraIdent.empty()) {
+ Diag(ExtraNamespaceLoc[0], diag::err_nested_namespaces_with_double_colon)
+ << SourceRange(ExtraNamespaceLoc.front(), ExtraIdentLoc.back());
+ }
Diag(Tok, Ident ? diag::err_expected_lbrace :
diag::err_expected_ident_lbrace);
return 0;
@@ -96,11 +109,44 @@
if (getCurScope()->isClassScope() || getCurScope()->isTemplateParamScope() ||
getCurScope()->isInObjcMethodScope() || getCurScope()->getBlockParent() ||
getCurScope()->getFnParent()) {
+ if (!ExtraIdent.empty()) {
+ Diag(ExtraNamespaceLoc[0], diag::err_nested_namespaces_with_double_colon)
+ << SourceRange(ExtraNamespaceLoc.front(), ExtraIdentLoc.back());
+ }
Diag(LBrace, diag::err_namespace_nonnamespace_scope);
SkipUntil(tok::r_brace, false);
return 0;
}
+ if (!ExtraIdent.empty()) {
+ TentativeParsingAction TPA(*this);
+ SkipUntil(tok::r_brace, /*StopAtSemi*/false, /*DontConsume*/true);
+ Token rBraceToken = Tok;
+ TPA.Revert();
+
+ if (!rBraceToken.is(tok::r_brace)) {
+ Diag(ExtraNamespaceLoc[0], diag::err_nested_namespaces_with_double_colon)
+ << SourceRange(ExtraNamespaceLoc.front(), ExtraIdentLoc.back());
+ } else {
+
+ std::string NamespaceFix = "";
+ for (std::vector<IdentifierInfo*>::iterator I = ExtraIdent.begin(),
+ E = ExtraIdent.end(); I != E; ++I) {
+ NamespaceFix += " { namespace ";
+ NamespaceFix += (*I)->getName();
+ }
+ std::string RBraces;
+ for (int i = 0; i < ExtraIdent.size(); ++i) {
+ RBraces += "} ";
+ }
+ Diag(ExtraNamespaceLoc[0], diag::err_nested_namespaces_with_double_colon)
+ << FixItHint::CreateReplacement(SourceRange(ExtraNamespaceLoc.front(),
+ ExtraIdentLoc.back()),
+ NamespaceFix)
+ << FixItHint::CreateInsertion(rBraceToken.getLocation(), RBraces);
+ }
+ }
+
// If we're still good, complain about inline namespaces in non-C++0x now.
if (!getLang().CPlusPlus0x && InlineLoc.isValid())
Diag(InlineLoc, diag::ext_inline_namespace);
@@ -115,23 +161,56 @@
PrettyDeclStackTraceEntry CrashInfo(Actions, NamespcDecl, NamespaceLoc,
"parsing namespace");
- while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
- ParsedAttributesWithRange attrs(AttrFactory);
- MaybeParseCXX0XAttributes(attrs);
- MaybeParseMicrosoftAttributes(attrs);
- ParseExternalDeclaration(attrs);
- }
+ SourceLocation RBraceLoc;
+ // Parse the contents of the namespace. This includes parsing recovery on
+ // any improperly nested namespaces.
+ ParseInnerNamespace(ExtraIdentLoc, ExtraIdent, ExtraNamespaceLoc, 0,
+ InlineLoc, LBrace, attrs, RBraceLoc);
// Leave the namespace scope.
NamespaceScope.Exit();
- SourceLocation RBraceLoc = MatchRHSPunctuation(tok::r_brace, LBrace);
Actions.ActOnFinishNamespaceDef(NamespcDecl, RBraceLoc);
DeclEnd = RBraceLoc;
return NamespcDecl;
}
+/// ParseInnerNamespace - Parse the contents of a namespace.
+void Parser::ParseInnerNamespace(std::vector<SourceLocation>& IdentLoc,
+ std::vector<IdentifierInfo*>& Ident,
+ std::vector<SourceLocation>& NamespaceLoc,
+ unsigned int index, SourceLocation& InlineLoc,
+ SourceLocation& LBrace,
+ ParsedAttributes& attrs,
+ SourceLocation& RBraceLoc) {
+ if (index == Ident.size()) {
+ while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
+ ParsedAttributesWithRange attrs(AttrFactory);
+ MaybeParseCXX0XAttributes(attrs);
+ MaybeParseMicrosoftAttributes(attrs);
+ ParseExternalDeclaration(attrs);
+ }
+ RBraceLoc = MatchRHSPunctuation(tok::r_brace, LBrace);
+
+ return;
+ }
+
+ // Parse improperly nested namespaces.
+ ParseScope NamespaceScope(this, Scope::DeclScope);
+ Decl *NamespcDecl =
+ Actions.ActOnStartNamespaceDef(getCurScope(), SourceLocation(),
+ NamespaceLoc[index], IdentLoc[index],
+ Ident[index], LBrace, attrs.getList());
+
+ ParseInnerNamespace(IdentLoc, Ident, NamespaceLoc, ++index, InlineLoc,
+ LBrace, attrs, RBraceLoc);
+
+ NamespaceScope.Exit();
+
+ Actions.ActOnFinishNamespaceDef(NamespcDecl, RBraceLoc);
+}
+
/// ParseNamespaceAlias - Parse the part after the '=' in a namespace
/// alias definition.
///
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits