diff options
Diffstat (limited to 'src/lib/corelib/language/propertydeclaration.cpp')
-rw-r--r-- | src/lib/corelib/language/propertydeclaration.cpp | 219 |
1 files changed, 215 insertions, 4 deletions
diff --git a/src/lib/corelib/language/propertydeclaration.cpp b/src/lib/corelib/language/propertydeclaration.cpp index 2dbe41afd..d56ab3bb0 100644 --- a/src/lib/corelib/language/propertydeclaration.cpp +++ b/src/lib/corelib/language/propertydeclaration.cpp @@ -40,11 +40,16 @@ #include "propertydeclaration.h" #include "deprecationinfo.h" -#include <api/languageinfo.h> +#include "filecontext.h" +#include "item.h" +#include "qualifiedid.h" +#include "value.h" +#include <api/languageinfo.h> +#include <loader/loaderutils.h> #include <logging/translator.h> - #include <tools/error.h> +#include <tools/setupprojectparameters.h> #include <tools/qttools.h> #include <tools/stringconstants.h> @@ -101,7 +106,6 @@ public: DeprecationInfo deprecationInfo; }; - PropertyDeclaration::PropertyDeclaration() : d(new PropertyDeclarationData) { @@ -275,7 +279,12 @@ void PropertyDeclaration::setDeprecationInfo(const DeprecationInfo &deprecationI d->deprecationInfo = deprecationInfo; } -// see also: EvaluatorScriptClass::convertToPropertyType() +ErrorInfo PropertyDeclaration::checkForDeprecation(DeprecationWarningMode mode, + const CodeLocation &loc, Logger &logger) const +{ + return deprecationInfo().checkForDeprecation(mode, name(), loc, false, logger); +} + QVariant PropertyDeclaration::convertToPropertyType(const QVariant &v, Type t, const QStringList &namePrefix, const QString &key) { @@ -299,5 +308,207 @@ QVariant PropertyDeclaration::convertToPropertyType(const QVariant &v, Type t, return c; } +QVariant PropertyDeclaration::typedNullValue() const +{ + switch (type()) { + case PropertyDeclaration::Boolean: + return typedNullVariant<bool>(); + case PropertyDeclaration::Integer: + return typedNullVariant<int>(); + case PropertyDeclaration::VariantList: + return typedNullVariant<QVariantList>(); + case PropertyDeclaration::String: + case PropertyDeclaration::Path: + return typedNullVariant<QString>(); + case PropertyDeclaration::StringList: + case PropertyDeclaration::PathList: + return typedNullVariant<QStringList>(); + default: + return {}; + } +} + +bool PropertyDeclaration::shouldCheckAllowedValues() const +{ + return isValid() + && (d->type == PropertyDeclaration::String || d->type == PropertyDeclaration::StringList) + && !d->allowedValues.empty(); +} + +void PropertyDeclaration::checkAllowedValues( + const QVariant &value, + const CodeLocation &loc, + const QString &key, + LoaderState &loaderState) const +{ + const auto type = d->type; + if (!shouldCheckAllowedValues()) + return; + + if (value.isNull()) + return; + + const auto &allowedValues = d->allowedValues; + + const auto checkValue = [&loc, &allowedValues, &key, &loaderState](const QString &value) + { + if (!allowedValues.contains(value)) { + const auto message = Tr::tr("Value '%1' is not allowed for property '%2'.") + .arg(value, key); + ErrorInfo error(message, loc); + handlePropertyError(error, loaderState.parameters(), loaderState.logger()); + } + }; + + if (type == PropertyDeclaration::StringList) { + const auto strings = value.toStringList(); + for (const auto &string: strings) { + checkValue(string); + } + } else if (type == PropertyDeclaration::String) { + checkValue(value.toString()); + } +} + +namespace { +class PropertyDeclarationCheck : public ValueHandler +{ +public: + PropertyDeclarationCheck(LoaderState &loaderState) : m_loaderState(loaderState) {} + void operator()(Item *item) + { + m_checkingProject = item->type() == ItemType::Project; + handleItem(item); + } + +private: + void handle(JSSourceValue *value) override + { + if (!value->createdByPropertiesBlock()) { + const ErrorInfo error(Tr::tr("Property '%1' is not declared.") + .arg(m_currentName), value->location()); + handlePropertyError(error, m_loaderState.parameters(), m_loaderState.logger()); + } + } + void handle(ItemValue *value) override + { + if (checkItemValue(value)) + handleItem(value->item()); + } + bool checkItemValue(ItemValue *value) + { + // TODO: Remove once QBS-1030 is fixed. + if (parentItem()->type() == ItemType::Artifact) + return false; + + if (parentItem()->type() == ItemType::Properties) + return false; + + // TODO: Check where the in-between module instances come from. + if (value->item()->type() == ItemType::ModuleInstancePlaceholder) { + for (auto it = m_parentItems.rbegin(); it != m_parentItems.rend(); ++it) { + if ((*it)->type() == ItemType::Group) + return false; + if ((*it)->type() == ItemType::ModulePrefix) + continue; + break; + } + } + + if (value->item()->type() != ItemType::ModuleInstance + && value->item()->type() != ItemType::ModulePrefix + && (!parentItem()->file() || !parentItem()->file()->idScope() + || !parentItem()->file()->idScope()->hasProperty(m_currentName)) + && !value->createdByPropertiesBlock()) { + CodeLocation location = value->location(); + for (int i = int(m_parentItems.size() - 1); !location.isValid() && i >= 0; --i) + location = m_parentItems.at(i)->location(); + const ErrorInfo error(Tr::tr("Item '%1' is not declared. " + "Did you forget to add a Depends item?") + .arg(m_currentModuleName.toString()), location); + handlePropertyError(error, m_loaderState.parameters(), m_loaderState.logger()); + return false; + } + + return true; + } + void handleItem(Item *item) + { + if (m_checkingProject && item->type() == ItemType::Product) + return; + if (!m_handledItems.insert(item).second) + return; + if (item->type() == ItemType::Module + || item->type() == ItemType::Export + || (item->type() == ItemType::ModuleInstance && !item->isPresentModule()) + || item->type() == ItemType::Properties + + // The Properties child of a SubProject item is not a regular item. + || item->type() == ItemType::PropertiesInSubProject + + || m_loaderState.topLevelProject().isDisabledItem(item)) { + return; + } + + m_parentItems.push_back(item); + for (Item::PropertyMap::const_iterator it = item->properties().constBegin(); + it != item->properties().constEnd(); ++it) { + if (item->type() == ItemType::Product && it.key() == StringConstants::moduleProviders() + && it.value()->type() == Value::ItemValueType) + continue; + const PropertyDeclaration decl = item->propertyDeclaration(it.key()); + if (decl.isValid()) { + const ErrorInfo deprecationError = decl.checkForDeprecation( + m_loaderState.parameters().deprecationWarningMode(), it.value()->location(), + m_loaderState.logger()); + if (deprecationError.hasError()) { + handlePropertyError(deprecationError, m_loaderState.parameters(), + m_loaderState.logger()); + } + continue; + } + m_currentName = it.key(); + const QualifiedId oldModuleName = m_currentModuleName; + if (parentItem()->type() != ItemType::ModulePrefix) + m_currentModuleName.clear(); + m_currentModuleName.push_back(m_currentName); + it.value()->apply(this); + m_currentModuleName = oldModuleName; + } + m_parentItems.pop_back(); + for (Item * const child : item->children()) { + switch (child->type()) { + case ItemType::Export: + case ItemType::Depends: + case ItemType::Parameter: + case ItemType::Parameters: + break; + case ItemType::Group: + if (item->type() == ItemType::Module || item->type() == ItemType::ModuleInstance) + break; + Q_FALLTHROUGH(); + default: + handleItem(child); + } + } + } + void handle(VariantValue *) override { /* only created internally - no need to check */ } + + Item *parentItem() const { return m_parentItems.back(); } + + LoaderState &m_loaderState; + Set<Item *> m_handledItems; + std::vector<Item *> m_parentItems; + QualifiedId m_currentModuleName; + QString m_currentName; + bool m_checkingProject = false; +}; +} // namespace + +void checkPropertyDeclarations(Item *topLevelItem, LoaderState &loaderState) +{ + (PropertyDeclarationCheck(loaderState))(topLevelItem); +} + } // namespace Internal } // namespace qbs |