From 9ee6068e20ad984f0df65a5615bdf6e352b0a667 Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Sun, 13 May 2018 02:29:40 -0400 Subject: [PATCH] Remove duplicate namespace aliases so they don't produce syntax errors. (#1222) * Remove duplicate namespace aliases so they don't produce syntax errors. DACA2 results showed new SymbolDatabase syntax errors when duplicate namespace aliases were simplified improperly. The solution is to remove them in the tokenizer when found. * Add tests for deleting namespace aliases at end of token list. * Use eraseTokens to delete multiple tokens at once. --- lib/tokenize.cpp | 47 +++++++++++++++++++++++++++++++++++++ test/testsimplifytokens.cpp | 32 +++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 84fd46764..b608d1fb7 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -10247,6 +10247,31 @@ void Tokenizer::simplifyNestedNamespace() } } +static bool sameTokens(const Token *first, const Token *last, const Token *other) +{ + while (other && first->str() == other->str()) { + if (first == last) + return true; + first = first->next(); + other = other->next(); + } + + return false; +} + +static Token * deleteAlias(Token * tok) +{ + Token::eraseTokens(tok, Token::findsimplematch(tok, ";")); + + // delete first token + tok->deleteThis(); + + // delete ';' if not last token + tok->deleteThis(); + + return tok; +} + void Tokenizer::simplifyNamespaceAliases() { if (!isCPP()) @@ -10281,6 +10306,28 @@ void Tokenizer::simplifyNamespaceAliases() else if (Token::simpleMatch(tok2, "}")) endScope--; else if (tok2->str() == name) { + if (Token::Match(tok2->previous(), "namespace %name% =")) { + // check for possible duplicate aliases + if (sameTokens(tokNameStart, tokNameEnd, tok2->tokAt(2))) { + // delete duplicate + tok2 = deleteAlias(tok2->previous()); + continue; + } else { + // conflicting declaration (syntax error) + if (endScope == scope) { + // delete conflicting declaration + tok2 = deleteAlias(tok2->previous()); + } + + // new declaration + else { + // TODO: use the new alias in this scope + tok2 = deleteAlias(tok2->previous()); + } + continue; + } + } + tok2->str(tokNameStart->str()); Token * tok3 = tokNameStart; while (tok3 != tokNameEnd) { diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index 5fd61ab41..79a026006 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -3546,6 +3546,38 @@ private: tok("using namespace std; namespace ios = boost::iostreams;")); ASSERT_EQUALS("namespace NS { boost :: iostreams :: istream foo ( \"foo\" ) ; }", tok("namespace NS { using namespace std; namespace ios = boost::iostreams; ios::istream foo(\"foo\"); }")); + + // duplicate namespace aliases + ASSERT_EQUALS(";", + tok("namespace ios = boost::iostreams;\nnamespace ios = boost::iostreams;")); + ASSERT_EQUALS(";", + tok("namespace ios = boost::iostreams;\nnamespace ios = boost::iostreams;\nnamespace ios = boost::iostreams;")); + ASSERT_EQUALS("namespace A { namespace B { void foo ( ) { bar ( A :: B :: ab ( ) ) ; } } }", + tok("namespace A::B {" + "namespace AB = A::B;" + "void foo() {" + " namespace AB = A::B;" // duplicate declaration + " bar(AB::ab());" + "}" + "namespace AB = A::B;" //duplicate declaration + "}")); + + // redeclared nested namespace aliases + TODO_ASSERT_EQUALS("namespace A { namespace B { void foo ( ) { bar ( A :: B :: ab ( ) ) ; { baz ( A :: a ( ) ) ; } bar ( A :: B :: ab ( ) ) ; } } }", + "namespace A { namespace B { void foo ( ) { bar ( A :: B :: ab ( ) ) ; { baz ( A :: B :: a ( ) ) ; } bar ( A :: B :: ab ( ) ) ; } } }", + tok("namespace A::B {" + "namespace AB = A::B;" + "void foo() {" + " namespace AB = A::B;" // duplicate declaration + " bar(AB::ab());" + " {" + " namespace AB = A;" + " baz(AB::a());" // redeclaration OK + " }" + " bar(AB::ab());" + "}" + "namespace AB = A::B;" //duplicate declaration + "}")); } };