Commit d0d9f87a by alokp@chromium.org

Make sure that #version occurs before anything else, except for comments and white space.

Review URL: https://codereview.appspot.com/6348056 git-svn-id: https://angleproject.googlecode.com/svn/trunk@1184 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent 2e81891c
...@@ -102,6 +102,9 @@ std::string Diagnostics::message(ID id) ...@@ -102,6 +102,9 @@ std::string Diagnostics::message(ID id)
return "invalid version number"; return "invalid version number";
case INVALID_VERSION_DIRECTIVE: case INVALID_VERSION_DIRECTIVE:
return "invalid version directive"; return "invalid version directive";
case VERSION_NOT_FIRST_STATEMENT:
return "#version directive must occur before anything else, "
"except for comments and white space";
case INVALID_LINE_NUMBER: case INVALID_LINE_NUMBER:
return "invalid line number"; return "invalid line number";
case INVALID_FILE_NUMBER: case INVALID_FILE_NUMBER:
......
...@@ -58,6 +58,7 @@ class Diagnostics ...@@ -58,6 +58,7 @@ class Diagnostics
INVALID_EXTENSION_DIRECTIVE, INVALID_EXTENSION_DIRECTIVE,
INVALID_VERSION_NUMBER, INVALID_VERSION_NUMBER,
INVALID_VERSION_DIRECTIVE, INVALID_VERSION_DIRECTIVE,
VERSION_NOT_FIRST_STATEMENT,
INVALID_LINE_NUMBER, INVALID_LINE_NUMBER,
INVALID_FILE_NUMBER, INVALID_FILE_NUMBER,
INVALID_LINE_DIRECTIVE, INVALID_LINE_DIRECTIVE,
......
...@@ -208,6 +208,7 @@ DirectiveParser::DirectiveParser(Tokenizer* tokenizer, ...@@ -208,6 +208,7 @@ DirectiveParser::DirectiveParser(Tokenizer* tokenizer,
MacroSet* macroSet, MacroSet* macroSet,
Diagnostics* diagnostics, Diagnostics* diagnostics,
DirectiveHandler* directiveHandler) : DirectiveHandler* directiveHandler) :
mPastFirstStatement(false),
mTokenizer(tokenizer), mTokenizer(tokenizer),
mMacroSet(macroSet), mMacroSet(macroSet),
mDiagnostics(diagnostics), mDiagnostics(diagnostics),
...@@ -224,6 +225,7 @@ void DirectiveParser::lex(Token* token) ...@@ -224,6 +225,7 @@ void DirectiveParser::lex(Token* token)
if (token->type == Token::PP_HASH) if (token->type == Token::PP_HASH)
{ {
parseDirective(token); parseDirective(token);
mPastFirstStatement = true;
} }
if (token->type == Token::LAST) if (token->type == Token::LAST)
...@@ -238,6 +240,8 @@ void DirectiveParser::lex(Token* token) ...@@ -238,6 +240,8 @@ void DirectiveParser::lex(Token* token)
} }
} while (skipping() || (token->type == '\n')); } while (skipping() || (token->type == '\n'));
mPastFirstStatement = true;
} }
void DirectiveParser::parseDirective(Token* token) void DirectiveParser::parseDirective(Token* token)
...@@ -245,6 +249,12 @@ void DirectiveParser::parseDirective(Token* token) ...@@ -245,6 +249,12 @@ void DirectiveParser::parseDirective(Token* token)
assert(token->type == Token::PP_HASH); assert(token->type == Token::PP_HASH);
mTokenizer->lex(token); mTokenizer->lex(token);
if (isEOD(token))
{
// Empty Directive.
return;
}
DirectiveType directive = getDirective(token); DirectiveType directive = getDirective(token);
// While in an excluded conditional block/group, // While in an excluded conditional block/group,
...@@ -688,6 +698,14 @@ void DirectiveParser::parseVersion(Token* token) ...@@ -688,6 +698,14 @@ void DirectiveParser::parseVersion(Token* token)
{ {
assert(getDirective(token) == DIRECTIVE_VERSION); assert(getDirective(token) == DIRECTIVE_VERSION);
if (mPastFirstStatement)
{
mDiagnostics->report(Diagnostics::VERSION_NOT_FIRST_STATEMENT,
token->location, token->text);
skipUntilEOD(mTokenizer, token);
return;
}
enum State enum State
{ {
VERSION_NUMBER VERSION_NUMBER
......
...@@ -69,6 +69,7 @@ class DirectiveParser : public Lexer ...@@ -69,6 +69,7 @@ class DirectiveParser : public Lexer
{ {
} }
}; };
bool mPastFirstStatement;
std::vector<ConditionalBlock> mConditionalStack; std::vector<ConditionalBlock> mConditionalStack;
Tokenizer* mTokenizer; Tokenizer* mTokenizer;
MacroSet* mMacroSet; MacroSet* mMacroSet;
......
...@@ -62,6 +62,138 @@ TEST_F(VersionTest, MissingNewline) ...@@ -62,6 +62,138 @@ TEST_F(VersionTest, MissingNewline)
preprocess(str, expected); preprocess(str, expected);
} }
TEST_F(VersionTest, AfterComments)
{
const char* str = "/* block comment acceptable */\n"
"// line comment acceptable\n"
"#version 200\n";
const char* expected = "\n\n\n";
using testing::_;
// Directive successfully parsed.
EXPECT_CALL(mDirectiveHandler,
handleVersion(pp::SourceLocation(0, 3), 200));
// No error or warning.
EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);
preprocess(str, expected);
}
TEST_F(VersionTest, AfterWhitespace)
{
const char* str = "\n"
"\n"
"#version 200\n";
const char* expected = "\n\n\n";
using testing::_;
// Directive successfully parsed.
EXPECT_CALL(mDirectiveHandler,
handleVersion(pp::SourceLocation(0, 3), 200));
// No error or warning.
EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);
preprocess(str, expected);
}
TEST_F(VersionTest, AfterValidToken)
{
const char* str = "foo\n"
"#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 2), _));
pp::Token token;
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
}
TEST_F(VersionTest, AfterInvalidToken)
{
const char* str = "$\n"
"#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::INVALID_CHARACTER,
pp::SourceLocation(0, 1), "$"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 2), _));
pp::Token token;
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
}
TEST_F(VersionTest, AfterValidDirective)
{
const char* str = "#\n"
"#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 2), _));
pp::Token token;
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
}
TEST_F(VersionTest, AfterInvalidDirective)
{
const char* str = "#foo\n"
"#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::DIRECTIVE_INVALID_NAME,
pp::SourceLocation(0, 1), "foo"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 2), _));
pp::Token token;
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
}
TEST_F(VersionTest, AfterExcludedBlock)
{
const char* str = "#if 0\n"
"foo\n"
"#endif\n"
"#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 4), _));
pp::Token token;
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
}
struct VersionTestParam struct VersionTestParam
{ {
const char* str; const char* str;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment