diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp index 55ada16196d..dcacb70e528 100644 --- a/src/shared/proparser/qmakebuiltins.cpp +++ b/src/shared/proparser/qmakebuiltins.cpp @@ -216,8 +216,11 @@ ProStringList QMakeEvaluator::evaluateExpandFunction( if (func_t == 0) { const QString &fn = func.toQString(m_tmp1); const QString &lfn = fn.toLower(); - if (!fn.isSharedWith(lfn)) + if (!fn.isSharedWith(lfn)) { func_t = ExpandFunc(statics.expands.value(ProString(lfn))); + if (func_t) + deprecationWarning(fL1S("Using uppercased builtin functions is deprecated.")); + } } ProStringList ret; @@ -974,23 +977,25 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( case T_INCLUDE: { if (m_skipLevel && !m_cumulative) return ReturnFalse; - QString parseInto; - // the third optional argument to include() controls warnings - // and is not used here - if ((args.count() == 2) || (args.count() == 3) ) { - parseInto = args.at(1).toQString(m_tmp2); - } else if (args.count() != 1) { - evalError(fL1S("include(file, into, silent) requires one, two or three arguments.")); + if (args.count() < 1 || args.count() > 3) { + evalError(fL1S("include(file, [into, [silent]]) requires one, two or three arguments.")); return ReturnFalse; } + QString parseInto; + LoadFlags flags = 0; + if (args.count() >= 2) { + parseInto = args.at(1).toQString(m_tmp2); + if (args.count() >= 3 && isTrue(args.at(2), m_tmp3)) + flags = LoadSilent; + } QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); fn.detach(); bool ok; if (parseInto.isEmpty()) { - ok = evaluateFile(fn, QMakeHandler::EvalIncludeFile, LoadProOnly); + ok = evaluateFile(fn, QMakeHandler::EvalIncludeFile, LoadProOnly | flags); } else { ProValueMap symbols; - if ((ok = evaluateFileInto(fn, QMakeHandler::EvalAuxFile, &symbols, LoadAll))) { + if ((ok = evaluateFileInto(fn, QMakeHandler::EvalAuxFile, &symbols, LoadAll | flags))) { ProValueMap newMap; for (ProValueMap::ConstIterator it = m_valuemapStack.top().constBegin(), @@ -1011,20 +1016,20 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( m_valuemapStack.top() = newMap; } } - return returnBool(ok); + return returnBool(ok || (flags & LoadSilent)); } case T_LOAD: { if (m_skipLevel && !m_cumulative) return ReturnFalse; - // bool ignore_error = false; + bool ignore_error = false; if (args.count() == 2) { - // ignore_error = isTrue(args.at(1), m_tmp2); + ignore_error = isTrue(args.at(1), m_tmp2); } else if (args.count() != 1) { evalError(fL1S("load(feature) requires one or two arguments.")); return ReturnFalse; } - // XXX ignore_error unused - return returnBool(evaluateFeatureFile(m_option->expandEnvVars(args.at(0).toQString()))); + return returnBool(evaluateFeatureFile(m_option->expandEnvVars(args.at(0).toQString()), + ignore_error) || ignore_error); } case T_DEBUG: // Yup - do nothing. Nothing is going to enable debug output anyway. diff --git a/src/shared/proparser/qmakeevaluator.cpp b/src/shared/proparser/qmakeevaluator.cpp index ddfe382c372..f7024eac9a9 100644 --- a/src/shared/proparser/qmakeevaluator.cpp +++ b/src/shared/proparser/qmakeevaluator.cpp @@ -138,7 +138,11 @@ void QMakeEvaluator::initStatics() const ProString &QMakeEvaluator::map(const ProString &var) { QHash::ConstIterator it = statics.varMap.constFind(var); - return (it != statics.varMap.constEnd()) ? it.value() : var; + if (it == statics.varMap.constEnd()) + return var; + deprecationWarning(fL1S("Variable %s is deprecated; use %s instead.") + .arg(var.toQString(), it.value().toQString())); + return it.value(); } @@ -239,7 +243,8 @@ ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFil ushort unicode; const QChar *vals_data = vals.data(); const int vals_len = vals.length(); - for (int x = 0, parens = 0; x < vals_len; x++) { + int parens = 0; + for (int x = 0; x < vals_len; x++) { unicode = vals_data[x].unicode(); if (x != (int)vals_len-1 && unicode == BACKSLASH && (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) { @@ -263,6 +268,8 @@ ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFil } if (!build.isEmpty()) ret << ProString(build, NoHash).setSource(source); + if (parens) + deprecationWarning(fL1S("Unmatched parentheses are deprecated.")); return ret; } @@ -780,6 +787,8 @@ void QMakeEvaluator::visitProVariable( if (!m_cumulative) { if (!m_skipLevel) { zipEmpty(&varVal); + // FIXME: add check+warning about accidental value removal. + // This may be a bit too noisy, though. m_valuemapStack.top()[varName] = varVal; } } else { @@ -1210,7 +1219,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile( if (!processed.contains(config)) { config.detach(); processed.insert(config); - if (evaluateFeatureFile(config)) { + if (evaluateFeatureFile(config, true)) { finished = false; break; } @@ -1892,6 +1901,8 @@ bool QMakeEvaluator::evaluateFileDirect( #endif return ok; } else { + if (!(flags & LoadSilent) && IoUtils::exists(fileName)) + languageWarning(fL1S("Include file %1 not found").arg(fileName)); return false; } } @@ -1909,7 +1920,7 @@ bool QMakeEvaluator::evaluateFile( return evaluateFileDirect(fileName, type, flags); } -bool QMakeEvaluator::evaluateFeatureFile(const QString &fileName) +bool QMakeEvaluator::evaluateFeatureFile(const QString &fileName, bool silent) { QString fn = fileName; if (!fn.endsWith(QLatin1String(".prf"))) @@ -1938,13 +1949,18 @@ bool QMakeEvaluator::evaluateFeatureFile(const QString &fileName) if (QFileInfo(fn).exists()) goto cool; #endif + if (!silent) + languageWarning(fL1S("Cannot find feature %1").arg(fileName)); return false; cool: ProStringList &already = valuesRef(ProString("QMAKE_INTERNAL_INCLUDED_FEATURES")); ProString afn(fn, NoHash); - if (already.contains(afn)) + if (already.contains(afn)) { + if (!silent) + languageWarning(fL1S("Feature %1 already included").arg(fileName)); return true; + } already.append(afn); #ifdef PROEVALUATOR_CUMULATIVE diff --git a/src/shared/proparser/qmakeevaluator.h b/src/shared/proparser/qmakeevaluator.h index 43e8523ab97..69b85b38a3d 100644 --- a/src/shared/proparser/qmakeevaluator.h +++ b/src/shared/proparser/qmakeevaluator.h @@ -53,6 +53,9 @@ public: enum { SourceEvaluator = 0x10, + EvalWarnLanguage = SourceEvaluator | WarningMessage | WarnLanguage, + EvalWarnDeprecated = SourceEvaluator | WarningMessage | WarnDeprecated, + EvalError = ErrorMessage | SourceEvaluator }; @@ -71,7 +74,8 @@ public: LoadProOnly = 0, LoadPreFiles = 1, LoadPostFiles = 2, - LoadAll = LoadPreFiles|LoadPostFiles + LoadAll = LoadPreFiles|LoadPostFiles, + LoadSilent = 0x10 }; Q_DECLARE_FLAGS(LoadFlags, LoadFlag) @@ -120,7 +124,7 @@ public: void visitProFunctionDef(ushort tok, const ProString &name, const ushort *tokPtr); void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr); - static const ProString &map(const ProString &var); + const ProString &map(const ProString &var); ProValueMap *findValues(const ProString &variableName, ProValueMap::Iterator *it); void setTemplate(); @@ -139,13 +143,17 @@ public: LoadFlags flags); bool evaluateFile(const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags); - bool evaluateFeatureFile(const QString &fileName); + bool evaluateFeatureFile(const QString &fileName, bool silent = false); bool evaluateFileInto(const QString &fileName, QMakeHandler::EvalFileType type, ProValueMap *values, // output-only LoadFlags flags); void message(int type, const QString &msg) const; void evalError(const QString &msg) const { message(QMakeHandler::EvalError, msg); } + void languageWarning(const QString &msg) const + { message(QMakeHandler::EvalWarnLanguage, msg); } + void deprecationWarning(const QString &msg) const + { message(QMakeHandler::EvalWarnDeprecated, msg); } QList prepareFunctionArgs(const ushort *&tokPtr); QList prepareFunctionArgs(const ProString &arguments); diff --git a/src/shared/proparser/qmakeparser.cpp b/src/shared/proparser/qmakeparser.cpp index 6858d9e9f13..800969a53ee 100644 --- a/src/shared/proparser/qmakeparser.cpp +++ b/src/shared/proparser/qmakeparser.cpp @@ -309,6 +309,7 @@ bool QMakeParser::read(ProFile *pro, const QString &in) int parens = 0; // Braces in value context int argc = 0; int wordCount = 0; // Number of words in currently accumulated expression + int lastIndent = 0; // Previous line's indentation, to detect accidental continuation abuse bool putSpace = false; // Only ever true inside quoted string bool lineMarked = true; // For in-expression markers ushort needSep = TokNewStr; // Complementary to putSpace: separator outside quotes @@ -372,7 +373,8 @@ bool QMakeParser::read(ProFile *pro, const QString &in) ushort c; // First, skip leading whitespace - for (;; ++cur) { + int indent; + for (indent = 0; ; ++cur, ++indent) { c = *cur; if (c == '\n') { ++cur; @@ -556,12 +558,14 @@ bool QMakeParser::read(ProFile *pro, const QString &in) needSep = 0; goto nextChr; } - } else if (c == '\\' && cur != end) { + } else if (c == '\\') { static const char symbols[] = "[]{}()$\\'\""; - ushort c2 = *cur; - if (!(c2 & 0xff00) && strchr(symbols, c2)) { + ushort c2; + if (cur != end && !((c2 = *cur) & 0xff00) && strchr(symbols, c2)) { c = c2; cur++; + } else { + deprecationWarning(fL1S("Unescaped backslashes are deprecated")); } } else if (quote) { if (c == quote) { @@ -722,6 +726,9 @@ bool QMakeParser::read(ProFile *pro, const QString &in) goto closeScope; } --parens; + } else if (c == '=') { + if (indent < lastIndent) + languageWarning(fL1S("Possible accidental line continuation")); } } if (putSpace) { @@ -771,6 +778,8 @@ bool QMakeParser::read(ProFile *pro, const QString &in) } } else if (context == CtxValue) { FLUSH_VALUE_LIST(); + if (parens) + languageWarning(fL1S("Possible braces mismatch")); } else { finalizeCond(tokPtr, buf, ptr, wordCount); } @@ -780,6 +789,7 @@ bool QMakeParser::read(ProFile *pro, const QString &in) goto freshLine; } + lastIndent = indent; lineMarked = false; ignore: cur = cptr; diff --git a/src/shared/proparser/qmakeparser.h b/src/shared/proparser/qmakeparser.h index 43d62d15e35..9c1a3866546 100644 --- a/src/shared/proparser/qmakeparser.h +++ b/src/shared/proparser/qmakeparser.h @@ -46,11 +46,19 @@ class QMAKE_EXPORT QMakeParserHandler public: enum { CategoryMask = 0xf00, + WarningMessage = 0x000, ErrorMessage = 0x100, SourceMask = 0xf0, SourceParser = 0, + CodeMask = 0xf, + WarnLanguage = 0, + WarnDeprecated, + + ParserWarnLanguage = SourceParser | WarningMessage | WarnLanguage, + ParserWarnDeprecated = SourceParser | WarningMessage | WarnDeprecated, + ParserIoError = ErrorMessage | SourceParser, ParserError }; @@ -123,6 +131,10 @@ private: void message(int type, const QString &msg) const; void parseError(const QString &msg) const { message(QMakeParserHandler::ParserError, msg); } + void languageWarning(const QString &msg) const + { message(QMakeParserHandler::ParserWarnLanguage, msg); } + void deprecationWarning(const QString &msg) const + { message(QMakeParserHandler::ParserWarnDeprecated, msg); } // Current location ProFile *m_proFile; diff --git a/tests/manual/proparser/main.cpp b/tests/manual/proparser/main.cpp index dd89628a48f..e7c4bafbf0b 100644 --- a/tests/manual/proparser/main.cpp +++ b/tests/manual/proparser/main.cpp @@ -43,18 +43,20 @@ #include #include -static void print(const QString &fileName, int lineNo, const QString &msg) +static void print(const QString &fileName, int lineNo, int type, const QString &msg) { + QString pfx = ((type & QMakeHandler::CategoryMask) == QMakeHandler::WarningMessage) + ? QString::fromLatin1("WARNING: ") : QString(); if (lineNo) - qWarning("%s(%d): %s", qPrintable(fileName), lineNo, qPrintable(msg)); + qWarning("%s%s:%d: %s", qPrintable(pfx), qPrintable(fileName), lineNo, qPrintable(msg)); else - qWarning("%s", qPrintable(msg)); + qWarning("%s%s", qPrintable(pfx), qPrintable(msg)); } class EvalHandler : public QMakeHandler { public: - virtual void message(int /* type */, const QString &msg, const QString &fileName, int lineNo) - { print(fileName, lineNo, msg); } + virtual void message(int type, const QString &msg, const QString &fileName, int lineNo) + { print(fileName, lineNo, type, msg); } virtual void fileMessage(const QString &msg) { qWarning("%s", qPrintable(msg)); }