forked from qt-creator/qt-creator
CMakePM: Integrate CMake's lexer
Kept only the needed bits to parse the CMake files. Change-Id: I7c4aa0779846fcee91469a38f84d801513e3aa63 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -7,41 +7,29 @@
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <cmsys/Encoding.hxx>
|
||||
#endif
|
||||
|
||||
#include "cmListFileLexer.h"
|
||||
#include "cmMessageType.h"
|
||||
#include "cmMessenger.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
|
||||
struct cmListFileParser
|
||||
{
|
||||
cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt,
|
||||
cmMessenger* messenger);
|
||||
cmListFileParser(cmListFile* lf, std::string &error);
|
||||
~cmListFileParser();
|
||||
cmListFileParser(const cmListFileParser&) = delete;
|
||||
cmListFileParser& operator=(const cmListFileParser&) = delete;
|
||||
void IssueFileOpenError(std::string const& text) const;
|
||||
void IssueError(std::string const& text) const;
|
||||
bool ParseFile(const char* filename);
|
||||
bool ParseString(const char* str, const char* virtual_filename);
|
||||
bool ParseString(const std::string &str, const std::string &virtual_filename);
|
||||
bool Parse();
|
||||
bool ParseFunction(const char* name, long line);
|
||||
bool AddArgument(cmListFileLexer_Token* token,
|
||||
cmListFileArgument::Delimiter delim);
|
||||
cm::optional<cmListFileContext> CheckNesting() const;
|
||||
cmListFile* ListFile;
|
||||
cmListFileBacktrace Backtrace;
|
||||
cmMessenger* Messenger;
|
||||
const char* FileName = nullptr;
|
||||
cmListFileLexer* Lexer;
|
||||
std::string FunctionName;
|
||||
long FunctionLine;
|
||||
long FunctionLineEnd;
|
||||
std::vector<cmListFileArgument> FunctionArguments;
|
||||
std::string &Error;
|
||||
enum
|
||||
{
|
||||
SeparationOkay,
|
||||
@@ -50,12 +38,10 @@ struct cmListFileParser
|
||||
} Separation;
|
||||
};
|
||||
|
||||
cmListFileParser::cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt,
|
||||
cmMessenger* messenger)
|
||||
: ListFile(lf)
|
||||
, Backtrace(std::move(lfbt))
|
||||
, Messenger(messenger)
|
||||
, Lexer(cmListFileLexer_New())
|
||||
cmListFileParser::cmListFileParser(cmListFile *lf, std::string &error)
|
||||
: ListFile(lf)
|
||||
, Lexer(cmListFileLexer_New())
|
||||
, Error(error)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -64,65 +50,19 @@ cmListFileParser::~cmListFileParser()
|
||||
cmListFileLexer_Delete(this->Lexer);
|
||||
}
|
||||
|
||||
void cmListFileParser::IssueFileOpenError(const std::string& text) const
|
||||
{
|
||||
this->Messenger->IssueMessage(MessageType::FATAL_ERROR, text,
|
||||
this->Backtrace);
|
||||
}
|
||||
|
||||
void cmListFileParser::IssueError(const std::string& text) const
|
||||
{
|
||||
cmListFileContext lfc;
|
||||
lfc.FilePath = this->FileName;
|
||||
lfc.Line = cmListFileLexer_GetCurrentLine(this->Lexer);
|
||||
cmListFileBacktrace lfbt = this->Backtrace;
|
||||
lfbt = lfbt.Push(lfc);
|
||||
this->Messenger->IssueMessage(MessageType::FATAL_ERROR, text, lfbt);
|
||||
cmSystemTools::SetFatalErrorOccurred();
|
||||
Error += text;
|
||||
Error += "\n";
|
||||
}
|
||||
|
||||
bool cmListFileParser::ParseFile(const char* filename)
|
||||
bool cmListFileParser::ParseString(const std::string &str,
|
||||
const std::string &virtual_filename)
|
||||
{
|
||||
this->FileName = filename;
|
||||
this->FileName = virtual_filename.c_str();
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string expandedFileName = cmsys::Encoding::ToNarrow(
|
||||
cmSystemTools::ConvertToWindowsExtendedPath(filename));
|
||||
filename = expandedFileName.c_str();
|
||||
#endif
|
||||
|
||||
// Open the file.
|
||||
cmListFileLexer_BOM bom;
|
||||
if (!cmListFileLexer_SetFileName(this->Lexer, filename, &bom)) {
|
||||
this->IssueFileOpenError("cmListFileCache: error can not open file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bom == cmListFileLexer_BOM_Broken) {
|
||||
cmListFileLexer_SetFileName(this->Lexer, nullptr, nullptr);
|
||||
this->IssueFileOpenError("Error while reading Byte-Order-Mark. "
|
||||
"File not seekable?");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the Byte-Order-Mark, if any.
|
||||
if (bom != cmListFileLexer_BOM_None && bom != cmListFileLexer_BOM_UTF8) {
|
||||
cmListFileLexer_SetFileName(this->Lexer, nullptr, nullptr);
|
||||
this->IssueFileOpenError(
|
||||
"File starts with a Byte-Order-Mark that is not UTF-8.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->Parse();
|
||||
}
|
||||
|
||||
bool cmListFileParser::ParseString(const char* str,
|
||||
const char* virtual_filename)
|
||||
{
|
||||
this->FileName = virtual_filename;
|
||||
|
||||
if (!cmListFileLexer_SetString(this->Lexer, str)) {
|
||||
this->IssueFileOpenError("cmListFileCache: cannot allocate buffer.");
|
||||
if (!cmListFileLexer_SetString(this->Lexer, str.c_str(), (int)str.size())) {
|
||||
this->IssueError("cmListFileCache: cannot allocate buffer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -138,7 +78,8 @@ bool cmListFileParser::Parse()
|
||||
if (token->type == cmListFileLexer_Token_Space) {
|
||||
} else if (token->type == cmListFileLexer_Token_Newline) {
|
||||
haveNewline = true;
|
||||
} else if (token->type == cmListFileLexer_Token_CommentBracket) {
|
||||
} else if (token->type == cmListFileLexer_Token_CommentBracket
|
||||
|| token->type == cmListFileLexer_Token_CommentLine) {
|
||||
haveNewline = false;
|
||||
} else if (token->type == cmListFileLexer_Token_Identifier) {
|
||||
if (haveNewline) {
|
||||
@@ -168,45 +109,15 @@ bool cmListFileParser::Parse()
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all functions are nested properly.
|
||||
if (auto badNesting = this->CheckNesting()) {
|
||||
this->Messenger->IssueMessage(
|
||||
MessageType::FATAL_ERROR,
|
||||
"Flow control statements are not properly nested.",
|
||||
this->Backtrace.Push(*badNesting));
|
||||
cmSystemTools::SetFatalErrorOccurred();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmListFile::ParseFile(const char* filename, cmMessenger* messenger,
|
||||
cmListFileBacktrace const& lfbt)
|
||||
{
|
||||
if (!cmSystemTools::FileExists(filename) ||
|
||||
cmSystemTools::FileIsDirectory(filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseError = false;
|
||||
|
||||
{
|
||||
cmListFileParser parser(this, lfbt, messenger);
|
||||
parseError = !parser.ParseFile(filename);
|
||||
}
|
||||
|
||||
return !parseError;
|
||||
}
|
||||
|
||||
bool cmListFile::ParseString(const char* str, const char* virtual_filename,
|
||||
cmMessenger* messenger,
|
||||
const cmListFileBacktrace& lfbt)
|
||||
bool cmListFile::ParseString(const std::string &str, const std::string& virtual_filename, std::string &error)
|
||||
{
|
||||
bool parseError = false;
|
||||
|
||||
{
|
||||
cmListFileParser parser(this, lfbt, messenger);
|
||||
cmListFileParser parser(this, error);
|
||||
parseError = !parser.ParseString(str, virtual_filename);
|
||||
}
|
||||
|
||||
@@ -279,6 +190,11 @@ bool cmListFileParser::ParseFunction(const char* name, long line)
|
||||
return false;
|
||||
}
|
||||
this->Separation = SeparationWarning;
|
||||
} else if (token->type == cmListFileLexer_Token_CommentLine) {
|
||||
if (!this->AddArgument(token, cmListFileArgument::Comment)) {
|
||||
return false;
|
||||
}
|
||||
this->Separation = SeparationWarning;
|
||||
} else if (token->type == cmListFileLexer_Token_ArgumentBracket) {
|
||||
if (!this->AddArgument(token, cmListFileArgument::Bracket)) {
|
||||
return false;
|
||||
@@ -299,210 +215,30 @@ bool cmListFileParser::ParseFunction(const char* name, long line)
|
||||
}
|
||||
|
||||
std::ostringstream error;
|
||||
cmListFileContext lfc;
|
||||
lfc.FilePath = this->FileName;
|
||||
lfc.Line = line;
|
||||
cmListFileBacktrace lfbt = this->Backtrace;
|
||||
lfbt = lfbt.Push(lfc);
|
||||
error << "Parse error. Function missing ending \")\". "
|
||||
<< "End of file reached.";
|
||||
this->Messenger->IssueMessage(MessageType::FATAL_ERROR, error.str(), lfbt);
|
||||
IssueError(error.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cmListFileParser::AddArgument(cmListFileLexer_Token* token,
|
||||
cmListFileArgument::Delimiter delim)
|
||||
{
|
||||
this->FunctionArguments.emplace_back(token->text, delim, token->line);
|
||||
this->FunctionArguments.emplace_back(token->text, delim, token->line, token->column);
|
||||
if (this->Separation == SeparationOkay) {
|
||||
return true;
|
||||
}
|
||||
bool isError = (this->Separation == SeparationError ||
|
||||
delim == cmListFileArgument::Bracket);
|
||||
std::ostringstream m;
|
||||
cmListFileContext lfc;
|
||||
lfc.FilePath = this->FileName;
|
||||
lfc.Line = token->line;
|
||||
cmListFileBacktrace lfbt = this->Backtrace;
|
||||
lfbt = lfbt.Push(lfc);
|
||||
|
||||
m << "Syntax " << (isError ? "Error" : "Warning") << " in cmake code at "
|
||||
<< "column " << token->column << "\n"
|
||||
<< "Argument not separated from preceding token by whitespace.";
|
||||
/* clang-format on */
|
||||
if (isError) {
|
||||
this->Messenger->IssueMessage(MessageType::FATAL_ERROR, m.str(), lfbt);
|
||||
IssueError(m.str());
|
||||
return false;
|
||||
}
|
||||
this->Messenger->IssueMessage(MessageType::AUTHOR_WARNING, m.str(), lfbt);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
enum class NestingStateEnum
|
||||
{
|
||||
If,
|
||||
Else,
|
||||
While,
|
||||
Foreach,
|
||||
Function,
|
||||
Macro,
|
||||
Block
|
||||
};
|
||||
|
||||
struct NestingState
|
||||
{
|
||||
NestingStateEnum State;
|
||||
cmListFileContext Context;
|
||||
};
|
||||
|
||||
bool TopIs(std::vector<NestingState>& stack, NestingStateEnum state)
|
||||
{
|
||||
return !stack.empty() && stack.back().State == state;
|
||||
}
|
||||
}
|
||||
|
||||
cm::optional<cmListFileContext> cmListFileParser::CheckNesting() const
|
||||
{
|
||||
std::vector<NestingState> stack;
|
||||
|
||||
for (auto const& func : this->ListFile->Functions) {
|
||||
auto const& name = func.LowerCaseName();
|
||||
if (name == "if") {
|
||||
stack.push_back({
|
||||
NestingStateEnum::If,
|
||||
cmListFileContext::FromListFileFunction(func, this->FileName),
|
||||
});
|
||||
} else if (name == "elseif") {
|
||||
if (!TopIs(stack, NestingStateEnum::If)) {
|
||||
return cmListFileContext::FromListFileFunction(func, this->FileName);
|
||||
}
|
||||
stack.back() = {
|
||||
NestingStateEnum::If,
|
||||
cmListFileContext::FromListFileFunction(func, this->FileName),
|
||||
};
|
||||
} else if (name == "else") {
|
||||
if (!TopIs(stack, NestingStateEnum::If)) {
|
||||
return cmListFileContext::FromListFileFunction(func, this->FileName);
|
||||
}
|
||||
stack.back() = {
|
||||
NestingStateEnum::Else,
|
||||
cmListFileContext::FromListFileFunction(func, this->FileName),
|
||||
};
|
||||
} else if (name == "endif") {
|
||||
if (!TopIs(stack, NestingStateEnum::If) &&
|
||||
!TopIs(stack, NestingStateEnum::Else)) {
|
||||
return cmListFileContext::FromListFileFunction(func, this->FileName);
|
||||
}
|
||||
stack.pop_back();
|
||||
} else if (name == "while") {
|
||||
stack.push_back({
|
||||
NestingStateEnum::While,
|
||||
cmListFileContext::FromListFileFunction(func, this->FileName),
|
||||
});
|
||||
} else if (name == "endwhile") {
|
||||
if (!TopIs(stack, NestingStateEnum::While)) {
|
||||
return cmListFileContext::FromListFileFunction(func, this->FileName);
|
||||
}
|
||||
stack.pop_back();
|
||||
} else if (name == "foreach") {
|
||||
stack.push_back({
|
||||
NestingStateEnum::Foreach,
|
||||
cmListFileContext::FromListFileFunction(func, this->FileName),
|
||||
});
|
||||
} else if (name == "endforeach") {
|
||||
if (!TopIs(stack, NestingStateEnum::Foreach)) {
|
||||
return cmListFileContext::FromListFileFunction(func, this->FileName);
|
||||
}
|
||||
stack.pop_back();
|
||||
} else if (name == "function") {
|
||||
stack.push_back({
|
||||
NestingStateEnum::Function,
|
||||
cmListFileContext::FromListFileFunction(func, this->FileName),
|
||||
});
|
||||
} else if (name == "endfunction") {
|
||||
if (!TopIs(stack, NestingStateEnum::Function)) {
|
||||
return cmListFileContext::FromListFileFunction(func, this->FileName);
|
||||
}
|
||||
stack.pop_back();
|
||||
} else if (name == "macro") {
|
||||
stack.push_back({
|
||||
NestingStateEnum::Macro,
|
||||
cmListFileContext::FromListFileFunction(func, this->FileName),
|
||||
});
|
||||
} else if (name == "endmacro") {
|
||||
if (!TopIs(stack, NestingStateEnum::Macro)) {
|
||||
return cmListFileContext::FromListFileFunction(func, this->FileName);
|
||||
}
|
||||
stack.pop_back();
|
||||
} else if (name == "block") {
|
||||
stack.push_back({
|
||||
NestingStateEnum::Block,
|
||||
cmListFileContext::FromListFileFunction(func, this->FileName),
|
||||
});
|
||||
} else if (name == "endblock") {
|
||||
if (!TopIs(stack, NestingStateEnum::Block)) {
|
||||
return cmListFileContext::FromListFileFunction(func, this->FileName);
|
||||
}
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
if (!stack.empty()) {
|
||||
return stack.back().Context;
|
||||
}
|
||||
|
||||
return cm::nullopt;
|
||||
}
|
||||
|
||||
#include "cmConstStack.tcc"
|
||||
template class cmConstStack<cmListFileContext, cmListFileBacktrace>;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, cmListFileContext const& lfc)
|
||||
{
|
||||
os << lfc.FilePath;
|
||||
if (lfc.Line > 0) {
|
||||
os << ":" << lfc.Line;
|
||||
if (!lfc.Name.empty()) {
|
||||
os << " (" << lfc.Name << ")";
|
||||
}
|
||||
} else if (lfc.Line == cmListFileContext::DeferPlaceholderLine) {
|
||||
os << ":DEFERRED";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
bool operator<(const cmListFileContext& lhs, const cmListFileContext& rhs)
|
||||
{
|
||||
if (lhs.Line != rhs.Line) {
|
||||
return lhs.Line < rhs.Line;
|
||||
}
|
||||
return lhs.FilePath < rhs.FilePath;
|
||||
}
|
||||
|
||||
bool operator==(const cmListFileContext& lhs, const cmListFileContext& rhs)
|
||||
{
|
||||
return lhs.Line == rhs.Line && lhs.FilePath == rhs.FilePath;
|
||||
}
|
||||
|
||||
bool operator!=(const cmListFileContext& lhs, const cmListFileContext& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, BT<std::string> const& s)
|
||||
{
|
||||
return os << s.Value;
|
||||
}
|
||||
|
||||
std::vector<BT<std::string>> cmExpandListWithBacktrace(
|
||||
std::string const& list, cmListFileBacktrace const& bt, bool emptyArgs)
|
||||
{
|
||||
std::vector<BT<std::string>> result;
|
||||
std::vector<std::string> tmp = cmExpandedList(list, emptyArgs);
|
||||
result.reserve(tmp.size());
|
||||
for (std::string& i : tmp) {
|
||||
result.emplace_back(std::move(i), bt);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user