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##.
Esse commit está contido em:
@@ -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<std::string>::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);
|
||||
|
||||
@@ -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<std::string> m_aliases;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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<std::string>::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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
@@ -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<LabelInfo> 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<std::string> m_aliases;
|
||||
|
||||
// for closure hidden name
|
||||
static Mutex s_mutex;
|
||||
static std::map<int64_t, int> s_closureIds;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário