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)
return "invalid version number";
case 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:
return "invalid line number";
case INVALID_FILE_NUMBER:
......
......@@ -58,6 +58,7 @@ class Diagnostics
INVALID_EXTENSION_DIRECTIVE,
INVALID_VERSION_NUMBER,
INVALID_VERSION_DIRECTIVE,
VERSION_NOT_FIRST_STATEMENT,
INVALID_LINE_NUMBER,
INVALID_FILE_NUMBER,
INVALID_LINE_DIRECTIVE,
......
......@@ -208,6 +208,7 @@ DirectiveParser::DirectiveParser(Tokenizer* tokenizer,
MacroSet* macroSet,
Diagnostics* diagnostics,
DirectiveHandler* directiveHandler) :
mPastFirstStatement(false),
mTokenizer(tokenizer),
mMacroSet(macroSet),
mDiagnostics(diagnostics),
......@@ -224,6 +225,7 @@ void DirectiveParser::lex(Token* token)
if (token->type == Token::PP_HASH)
{
parseDirective(token);
mPastFirstStatement = true;
}
if (token->type == Token::LAST)
......@@ -238,6 +240,8 @@ void DirectiveParser::lex(Token* token)
}
} while (skipping() || (token->type == '\n'));
mPastFirstStatement = true;
}
void DirectiveParser::parseDirective(Token* token)
......@@ -245,6 +249,12 @@ void DirectiveParser::parseDirective(Token* token)
assert(token->type == Token::PP_HASH);
mTokenizer->lex(token);
if (isEOD(token))
{
// Empty Directive.
return;
}
DirectiveType directive = getDirective(token);
// While in an excluded conditional block/group,
......@@ -688,6 +698,14 @@ void DirectiveParser::parseVersion(Token* token)
{
assert(getDirective(token) == DIRECTIVE_VERSION);
if (mPastFirstStatement)
{
mDiagnostics->report(Diagnostics::VERSION_NOT_FIRST_STATEMENT,
token->location, token->text);
skipUntilEOD(mTokenizer, token);
return;
}
enum State
{
VERSION_NUMBER
......
......@@ -69,6 +69,7 @@ class DirectiveParser : public Lexer
{
}
};
bool mPastFirstStatement;
std::vector<ConditionalBlock> mConditionalStack;
Tokenizer* mTokenizer;
MacroSet* mMacroSet;
......
......@@ -62,6 +62,138 @@ TEST_F(VersionTest, MissingNewline)
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
{
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