aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/language/propertydeclaration.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/corelib/language/propertydeclaration.cpp')
-rw-r--r--src/lib/corelib/language/propertydeclaration.cpp219
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