From 1e0f032ba7f0a0251b00fb577376264960395941 Mon Sep 17 00:00:00 2001 From: Paul Tarjan Date: Fri, 10 May 2013 23:55:15 -0700 Subject: [PATCH] absorb comments on namespaces This test didn't really intentionally test this, but it caught us not eating comments on namespaces. I don't understand the difference between ##ParserBase## and ##Parser##. Maybe this belongs here? It needed to be here to have access to ##pushComment##. --- hphp/compiler/parser/parser.cpp | 109 +++++++++++++++++- hphp/compiler/parser/parser.h | 20 ++++ .../test/zend/{bad => good}/zend/bug55156.php | 0 .../{bad => good}/zend/bug55156.php.expectf | 0 hphp/util/parser/parser.cpp | 108 +---------------- hphp/util/parser/parser.h | 20 ---- hphp/util/parser/test/parser.h | 20 ++++ 7 files changed, 149 insertions(+), 128 deletions(-) rename hphp/test/zend/{bad => good}/zend/bug55156.php (100%) rename hphp/test/zend/{bad => good}/zend/bug55156.php.expectf (100%) diff --git a/hphp/compiler/parser/parser.cpp b/hphp/compiler/parser/parser.cpp index ad425d37b..156603cc9 100644 --- a/hphp/compiler/parser/parser.cpp +++ b/hphp/compiler/parser/parser.cpp @@ -157,7 +157,7 @@ StatementListPtr Parser::ParseString(CStrRef input, AnalysisResultPtr ar, Parser::Parser(Scanner &scanner, const char *fileName, AnalysisResultPtr ar, int fileSize /* = 0 */) : ParserBase(scanner, fileName), m_ar(ar), m_lambdaMode(false), - m_closureGenerator(false) { + m_closureGenerator(false), m_nsState(SeenNothing) { string md5str = Eval::FileRepository::unitMd5(scanner.getMd5()); MD5 md5 = MD5(md5str.c_str()); @@ -1650,6 +1650,113 @@ void Parser::onTypeSpecialization(Token& type, char specialization) { } } +/////////////////////////////////////////////////////////////////////////////// +// namespace support + +void Parser::nns(int token) { + if (m_nsState == SeenNamespaceStatement && token != ';') { + error("No code may exist outside of namespace {}: %s", + getMessage().c_str()); + return; + } + if (m_nsState == SeenNothing && token != T_DECLARE) { + m_nsState = SeenNonNamespaceStatement; + } +} + +void Parser::onNamespaceStart(const std::string &ns, + bool file_scope /* =false */) { + if (m_nsState == SeenNonNamespaceStatement) { + error("Namespace declaration statement has to be the very first " + "statement in the script: %s", getMessage().c_str()); + return; + } + if (m_nsState != SeenNothing && file_scope != m_nsFileScope) { + error("Cannot mix bracketed namespace declarations with unbracketed " + "namespace declarations"); + } + + m_nsState = InsideNamespace; + m_nsFileScope = file_scope; + m_aliases.clear(); + pushComment(); + + m_namespace = ns; +} + +void Parser::onNamespaceEnd() { + m_nsState = SeenNamespaceStatement; +} + +void Parser::onUse(const std::string &ns, const std::string &as) { + if (m_aliases.find(as) != m_aliases.end()) { + error("Cannot use %s as %s because the name is already in use: %s", + ns.c_str(), as.c_str(), getMessage().c_str()); + return; + } + string key = as; + if (key.empty()) { + size_t pos = ns.rfind(NAMESPACE_SEP); + if (pos == string::npos) { + key = ns; + } else { + key = ns.substr(pos + 1); + } + } + m_aliases[key] = ns; +} + +std::string Parser::nsDecl(const std::string &name) { + if (m_namespace.empty()) { + return name; + } + return m_namespace + NAMESPACE_SEP + name; +} + +std::string Parser::resolve(const std::string &ns, bool cls) { + // try import rules first + string alias = ns; + size_t pos = ns.find(NAMESPACE_SEP); + if (pos != string::npos) { + alias = ns.substr(0, pos); + } + hphp_string_imap::const_iterator iter = m_aliases.find(alias); + if (iter != m_aliases.end()) { + if (pos != string::npos) { + return iter->second + ns.substr(pos); + } + return iter->second; + } + + // Classes are special. They don't fallback to the global namespace. + if (cls) { + if (!strcasecmp("self", ns.c_str()) || + !strcasecmp("parent", ns.c_str())) { + return ns; + } + // Don't prefix with \ because that isn't the real classname and we don't + // need a flag to signal fallback. + return nsDecl(ns); + } + + // if qualified name, prepend current namespace + if (pos != string::npos) { + return nsDecl(ns); + } + + // unqualified name in global namespace + if (m_namespace.empty()) { + return ns; + } + + if (!strcasecmp("true", ns.c_str()) || + !strcasecmp("false", ns.c_str()) || + !strcasecmp("null", ns.c_str())) { + return ns; + } + return nsDecl(ns); +} + void Parser::invalidateGoto(TStatementPtr stmt, GotoError error) { GotoStatement *gs = (GotoStatement*) stmt; assert(gs); diff --git a/hphp/compiler/parser/parser.h b/hphp/compiler/parser/parser.h index 6853d2c45..1b148659c 100644 --- a/hphp/compiler/parser/parser.h +++ b/hphp/compiler/parser/parser.h @@ -254,6 +254,14 @@ public: void onTypeList(Token& type1, const Token& type2); void onTypeSpecialization(Token& type, char specialization); + // for namespace support + void onNamespaceStart(const std::string &ns, bool file_scope = false); + void onNamespaceEnd(); + void onUse(const std::string &ns, const std::string &as); + void nns(int token = 0); + std::string nsDecl(const std::string &name); + std::string resolve(const std::string &ns, bool cls); + virtual void invalidateGoto(TStatementPtr stmt, GotoError error); virtual void invalidateLabel(TStatementPtr stmt); @@ -325,6 +333,18 @@ private: void checkAssignThis(Token &var); void addStatement(StatementPtr stmt, StatementPtr new_stmt); + + // for namespace support + enum NamespaceState { + SeenNothing, + SeenNonNamespaceStatement, + SeenNamespaceStatement, + InsideNamespace, + }; + NamespaceState m_nsState; + bool m_nsFileScope; + std::string m_namespace; // current namespace + hphp_string_imap m_aliases; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/test/zend/bad/zend/bug55156.php b/hphp/test/zend/good/zend/bug55156.php similarity index 100% rename from hphp/test/zend/bad/zend/bug55156.php rename to hphp/test/zend/good/zend/bug55156.php diff --git a/hphp/test/zend/bad/zend/bug55156.php.expectf b/hphp/test/zend/good/zend/bug55156.php.expectf similarity index 100% rename from hphp/test/zend/bad/zend/bug55156.php.expectf rename to hphp/test/zend/good/zend/bug55156.php.expectf diff --git a/hphp/util/parser/parser.cpp b/hphp/util/parser/parser.cpp index 579a4adae..042325bfb 100644 --- a/hphp/util/parser/parser.cpp +++ b/hphp/util/parser/parser.cpp @@ -73,7 +73,7 @@ void ParserBase::Reset() { /////////////////////////////////////////////////////////////////////////////// ParserBase::ParserBase(Scanner &scanner, const char *fileName) - : m_scanner(scanner), m_fileName(fileName), m_nsState(SeenNothing) { + : m_scanner(scanner), m_fileName(fileName) { if (m_fileName == nullptr) m_fileName = ""; // global scope @@ -284,111 +284,5 @@ void ParserBase::popLabelInfo() { m_labelInfos.pop_back(); } -/////////////////////////////////////////////////////////////////////////////// -// namespace support - -void ParserBase::nns(int token) { - if (m_nsState == SeenNamespaceStatement && token != ';') { - error("No code may exist outside of namespace {}: %d %s", - token, getMessage().c_str()); - return; - } - if (m_nsState == SeenNothing && token != T_DECLARE) { - m_nsState = SeenNonNamespaceStatement; - } -} - -void ParserBase::onNamespaceStart(const std::string &ns, - bool file_scope /* =false */) { - if (m_nsState == SeenNonNamespaceStatement) { - error("Namespace declaration statement has to be the very first " - "statement in the script: %s", getMessage().c_str()); - return; - } - if (m_nsState != SeenNothing && file_scope != m_nsFileScope) { - error("Cannot mix bracketed namespace declarations with unbracketed " - "namespace declarations"); - } - - m_nsState = InsideNamespace; - m_nsFileScope = file_scope; - m_aliases.clear(); - - m_namespace = ns; -} - -void ParserBase::onNamespaceEnd() { - m_nsState = SeenNamespaceStatement; -} - -void ParserBase::onUse(const std::string &ns, const std::string &as) { - if (m_aliases.find(as) != m_aliases.end()) { - error("Cannot use %s as %s because the name is already in use: %s", - ns.c_str(), as.c_str(), getMessage().c_str()); - return; - } - string key = as; - if (key.empty()) { - size_t pos = ns.rfind(NAMESPACE_SEP); - if (pos == string::npos) { - key = ns; - } else { - key = ns.substr(pos + 1); - } - } - m_aliases[key] = ns; -} - -std::string ParserBase::nsDecl(const std::string &name) { - if (m_namespace.empty()) { - return name; - } - return m_namespace + NAMESPACE_SEP + name; -} - -std::string ParserBase::resolve(const std::string &ns, bool cls) { - // try import rules first - string alias = ns; - size_t pos = ns.find(NAMESPACE_SEP); - if (pos != string::npos) { - alias = ns.substr(0, pos); - } - hphp_string_imap::const_iterator iter = m_aliases.find(alias); - if (iter != m_aliases.end()) { - if (pos != string::npos) { - return iter->second + ns.substr(pos); - } - return iter->second; - } - - // Classes are special. They don't fallback to the global namespace. - if (cls) { - if (!strcasecmp("self", ns.c_str()) || - !strcasecmp("parent", ns.c_str())) { - return ns; - } - // Don't prefix with \ because that isn't the real classname and we don't - // need a flag to signal fallback. - return nsDecl(ns); - } - - // if qualified name, prepend current namespace - if (pos != string::npos) { - return nsDecl(ns); - } - - // unqualified name in global namespace - if (m_namespace.empty()) { - return ns; - } - - if (!strcasecmp("true", ns.c_str()) || - !strcasecmp("false", ns.c_str()) || - !strcasecmp("null", ns.c_str())) { - return ns; - } - return nsDecl(ns); -} - /////////////////////////////////////////////////////////////////////////////// } diff --git a/hphp/util/parser/parser.h b/hphp/util/parser/parser.h index a0e43264b..c4435a297 100644 --- a/hphp/util/parser/parser.h +++ b/hphp/util/parser/parser.h @@ -168,14 +168,6 @@ public: ScannerToken *stmt); void popLabelInfo(); - // for namespace support - void onNamespaceStart(const std::string &ns, bool file_scope = false); - void onNamespaceEnd(); - void onUse(const std::string &ns, const std::string &as); - void nns(int token = 0); - std::string nsDecl(const std::string &name); - std::string resolve(const std::string &ns, bool cls); - enum GotoError { UndefLabel = 1, InvalidBlock, @@ -228,18 +220,6 @@ protected: }; std::vector m_labelInfos; // stack by function - // for namespace support - enum NamespaceState { - SeenNothing, - SeenNonNamespaceStatement, - SeenNamespaceStatement, - InsideNamespace, - }; - NamespaceState m_nsState; - bool m_nsFileScope; - std::string m_namespace; // current namespace - hphp_string_imap m_aliases; - // for closure hidden name static Mutex s_mutex; static std::map s_closureIds; diff --git a/hphp/util/parser/test/parser.h b/hphp/util/parser/test/parser.h index 71b2e2896..caeee385d 100644 --- a/hphp/util/parser/test/parser.h +++ b/hphp/util/parser/test/parser.h @@ -515,6 +515,26 @@ struct Parser : ParserBase { void onTypeSpecialization(const Token& type, char specialization) { X(type, specialization); } + + // for namespace support + void onNamespaceStart(const std::string &ns, bool file_scope = false) { + X(ns, file_scope); + } + void onNamespaceEnd() {} + void onUse(const std::string &ns, const std::string &as) { + X(ns, as); + } + void nns(bool declare = false) { + X(declare); + } + std::string nsDecl(const std::string &name) { + X(name); + return name; + } + std::string resolve(const std::string &ns, bool cls) { + X(ns, cls); + return ns; + } }; //////////////////////////////////////////////////////////////////////