#14912 Reject duplicate generated Python enum names

This commit is contained in:
Kristian Bendiksen committed 2026-10-23 15:21:23 +02:00
1 parent a3cd973da0
commit 5b229cdf32
20 files changed
+223 -121

No files matched your search

@@ -0,0 +1,47 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
namespace RimCompletionEnums
{
enum class ReferenceMDType
{
AUTO_REFERENCE_MD = 0,
MANUAL_REFERENCE_MD
};
enum class PressureDropType
{
HYDROSTATIC,
HYDROSTATIC_FRICTION,
HYDROSTATIC_FRICTION_ACCELERATION
};
enum class LengthAndDepthType
{
ABS,
INC
};
enum NonDarcyFlowType
{
NON_DARCY_NONE,
NON_DARCY_COMPUTED,
NON_DARCY_USER_DEFINED,
};
} // namespace RimCompletionEnums
@@ -78,16 +78,6 @@ void caf::AppEnum<RimFractureTemplate::WidthEnum>::setUp()
setDefault( RimFractureTemplate::WIDTH_FROM_FRACTURE );
}
template <>
void caf::AppEnum<RimFractureTemplate::NonDarcyFlowEnum>::setUp()
{
addItem( RimFractureTemplate::NON_DARCY_NONE, "None", "None" );
addItem( RimFractureTemplate::NON_DARCY_COMPUTED, "Computed", "Compute D-factor" );
addItem( RimFractureTemplate::NON_DARCY_USER_DEFINED, "UserDefined", "User Defined D-factor" );
setDefault( RimFractureTemplate::NON_DARCY_NONE );
}
template <>
void caf::AppEnum<RimFractureTemplate::BetaFactorEnum>::setUp()
{
@@ -18,6 +18,8 @@
#pragma once
#include "RimCompletionEnums.h"
#include "RiaDefines.h"
#include "cafAppEnum.h"
@@ -115,12 +117,10 @@ public:
BETA_FACTOR_FROM_FRACTURE,
};
enum NonDarcyFlowEnum
{
NON_DARCY_NONE,
NON_DARCY_COMPUTED,
NON_DARCY_USER_DEFINED,
};
using NonDarcyFlowEnum = RimCompletionEnums::NonDarcyFlowType;
static constexpr NonDarcyFlowEnum NON_DARCY_NONE = RimCompletionEnums::NON_DARCY_NONE;
static constexpr NonDarcyFlowEnum NON_DARCY_COMPUTED = RimCompletionEnums::NON_DARCY_COMPUTED;
static constexpr NonDarcyFlowEnum NON_DARCY_USER_DEFINED = RimCompletionEnums::NON_DARCY_USER_DEFINED;
public:
RimFractureTemplate();
@@ -37,28 +37,28 @@
namespace caf
{
template <>
void RimMswCompletionParameters::ReferenceMDEnum::setUp()
void AppEnum<RimCompletionEnums::ReferenceMDType>::setUp()
{
addItem( RimMswCompletionParameters::ReferenceMDType::AUTO_REFERENCE_MD, "GridEntryPoint", "Grid Entry Point", { "GridIntersectionRefMD" } );
addItem( RimMswCompletionParameters::ReferenceMDType::MANUAL_REFERENCE_MD, "UserDefined", "User Defined", { "ManualRefMD" } );
setDefault( RimMswCompletionParameters::ReferenceMDType::AUTO_REFERENCE_MD );
addItem( RimCompletionEnums::ReferenceMDType::AUTO_REFERENCE_MD, "GridEntryPoint", "Grid Entry Point", { "GridIntersectionRefMD" } );
addItem( RimCompletionEnums::ReferenceMDType::MANUAL_REFERENCE_MD, "UserDefined", "User Defined", { "ManualRefMD" } );
setDefault( RimCompletionEnums::ReferenceMDType::AUTO_REFERENCE_MD );
}
template <>
void RimMswCompletionParameters::PressureDropEnum::setUp()
void AppEnum<RimCompletionEnums::PressureDropType>::setUp()
{
addItem( RimMswCompletionParameters::PressureDropType::HYDROSTATIC, "H--", "Hydrostatic" );
addItem( RimMswCompletionParameters::PressureDropType::HYDROSTATIC_FRICTION, "HF-", "Hydrostatic + Friction" );
addItem( RimMswCompletionParameters::PressureDropType::HYDROSTATIC_FRICTION_ACCELERATION, "HFA", "Hydrostatic + Friction + Acceleration" );
setDefault( RimMswCompletionParameters::PressureDropType::HYDROSTATIC_FRICTION );
addItem( RimCompletionEnums::PressureDropType::HYDROSTATIC, "H--", "Hydrostatic" );
addItem( RimCompletionEnums::PressureDropType::HYDROSTATIC_FRICTION, "HF-", "Hydrostatic + Friction" );
addItem( RimCompletionEnums::PressureDropType::HYDROSTATIC_FRICTION_ACCELERATION, "HFA", "Hydrostatic + Friction + Acceleration" );
setDefault( RimCompletionEnums::PressureDropType::HYDROSTATIC_FRICTION );
}
template <>
void RimMswCompletionParameters::LengthAndDepthEnum::setUp()
void AppEnum<RimCompletionEnums::LengthAndDepthType>::setUp()
{
addItem( RimMswCompletionParameters::LengthAndDepthType::INC, "INC", "Incremental" );
addItem( RimMswCompletionParameters::LengthAndDepthType::ABS, "ABS", "Absolute" );
setDefault( RimMswCompletionParameters::LengthAndDepthType::ABS );
addItem( RimCompletionEnums::LengthAndDepthType::INC, "INC", "Incremental" );
addItem( RimCompletionEnums::LengthAndDepthType::ABS, "ABS", "Absolute" );
setDefault( RimCompletionEnums::LengthAndDepthType::ABS );
}
template <>
@@ -18,6 +18,7 @@
#pragma once
#include "RiaDefines.h"
#include "RimCompletionEnums.h"
#include "cafPdmChildField.h"
#include "cafPdmField.h"
@@ -34,24 +35,9 @@ class RimMswCompletionParameters : public caf::PdmObject
CAF_PDM_HEADER_INIT;
public:
enum class ReferenceMDType
{
AUTO_REFERENCE_MD = 0,
MANUAL_REFERENCE_MD
};
enum class PressureDropType
{
HYDROSTATIC,
HYDROSTATIC_FRICTION,
HYDROSTATIC_FRICTION_ACCELERATION
};
enum class LengthAndDepthType
{
ABS,
INC
};
using ReferenceMDType = RimCompletionEnums::ReferenceMDType;
using PressureDropType = RimCompletionEnums::PressureDropType;
using LengthAndDepthType = RimCompletionEnums::LengthAndDepthType;
enum class DiameterRoughnessMode
{
@@ -28,13 +28,13 @@
namespace caf
{
template <>
void caf::AppEnum<RimNonDarcyPerforationParameters::NonDarcyFlowEnum>::setUp()
void caf::AppEnum<RimCompletionEnums::NonDarcyFlowType>::setUp()
{
addItem( RimNonDarcyPerforationParameters::NON_DARCY_NONE, "None", "None" );
addItem( RimNonDarcyPerforationParameters::NON_DARCY_COMPUTED, "Computed", "Compute D-factor" );
addItem( RimNonDarcyPerforationParameters::NON_DARCY_USER_DEFINED, "UserDefined", "User Defined D-factor" );
addItem( RimCompletionEnums::NON_DARCY_NONE, "None", "None" );
addItem( RimCompletionEnums::NON_DARCY_COMPUTED, "Computed", "Compute D-factor" );
addItem( RimCompletionEnums::NON_DARCY_USER_DEFINED, "UserDefined", "User Defined D-factor" );
setDefault( RimNonDarcyPerforationParameters::NON_DARCY_NONE );
setDefault( RimCompletionEnums::NON_DARCY_NONE );
}
} // namespace caf
@@ -17,6 +17,8 @@
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RimCompletionEnums.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
#include "cafPdmUiGroup.h"
@@ -26,12 +28,10 @@ class RimNonDarcyPerforationParameters : public caf::PdmObject
CAF_PDM_HEADER_INIT;
public:
enum NonDarcyFlowEnum
{
NON_DARCY_NONE,
NON_DARCY_COMPUTED,
NON_DARCY_USER_DEFINED,
};
using NonDarcyFlowEnum = RimCompletionEnums::NonDarcyFlowType;
static constexpr NonDarcyFlowEnum NON_DARCY_NONE = RimCompletionEnums::NON_DARCY_NONE;
static constexpr NonDarcyFlowEnum NON_DARCY_COMPUTED = RimCompletionEnums::NON_DARCY_COMPUTED;
static constexpr NonDarcyFlowEnum NON_DARCY_USER_DEFINED = RimCompletionEnums::NON_DARCY_USER_DEFINED;
RimNonDarcyPerforationParameters();
~RimNonDarcyPerforationParameters() override;
@@ -43,14 +43,6 @@ namespace caf
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
template <>
void RimSegmentCollection::ReferenceMDEnum::setUp()
{
addItem( RimSegmentCollection::ReferenceMDType::AUTO_REFERENCE_MD, "GridEntryPoint", "Grid Entry Point", { "GridIntersectionRefMD" } );
addItem( RimSegmentCollection::ReferenceMDType::MANUAL_REFERENCE_MD, "UserDefined", "User Defined", { "ManualRefMD" } );
setDefault( RimSegmentCollection::ReferenceMDType::AUTO_REFERENCE_MD );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -18,6 +18,7 @@
#pragma once
#include "RiaDefines.h"
#include "RimCompletionEnums.h"
#include "cafPdmChildArrayField.h"
#include "cafPdmField.h"
@@ -41,11 +42,7 @@ class RimSegmentCollection : public caf::PdmObject
CAF_PDM_HEADER_INIT;
public:
enum class ReferenceMDType
{
AUTO_REFERENCE_MD = 0,
MANUAL_REFERENCE_MD
};
using ReferenceMDType = RimCompletionEnums::ReferenceMDType;
enum class CompsegsSegmentNumberType
{
@@ -74,23 +74,6 @@ void RimWellPathCompletionSettings::HydrostaticDensityEnum::setUp()
setDefault( RimWellPathCompletionSettings::SEGMENTED );
}
template <>
void RimWellPathCompletionSettings::PressureDropEnum::setUp()
{
addItem( RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC, "H--", "Hydrostatic" );
addItem( RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC_FRICTION, "HF-", "Hydrostatic + Friction" );
addItem( RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC_FRICTION_ACCELERATION, "HFA", "Hydrostatic + Friction + Acceleration" );
setDefault( RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC_FRICTION );
}
template <>
void RimWellPathCompletionSettings::LengthAndDepthEnum::setUp()
{
addItem( RimWellPathCompletionSettings::LengthAndDepthType::INC, "INC", "Incremental" );
addItem( RimWellPathCompletionSettings::LengthAndDepthType::ABS, "ABS", "Absolute" );
setDefault( RimWellPathCompletionSettings::LengthAndDepthType::ABS );
}
} // namespace caf
namespace
@@ -17,6 +17,8 @@
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RimCompletionEnums.h"
#include "cafPdmChildField.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
@@ -65,19 +67,10 @@ public:
};
using HydrostaticDensityEnum = caf::AppEnum<HydrostaticDensity>;
enum class PressureDropType
{
HYDROSTATIC,
HYDROSTATIC_FRICTION,
HYDROSTATIC_FRICTION_ACCELERATION
};
using PressureDropType = RimCompletionEnums::PressureDropType;
using PressureDropEnum = caf::AppEnum<PressureDropType>;
enum class LengthAndDepthType
{
ABS,
INC
};
using LengthAndDepthType = RimCompletionEnums::LengthAndDepthType;
using LengthAndDepthEnum = caf::AppEnum<LengthAndDepthType>;
public:
@@ -0,0 +1,27 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
namespace RimWellEventEnums
{
enum class State
{
OPEN,
SHUT
};
} // namespace RimWellEventEnums
@@ -27,11 +27,11 @@
namespace caf
{
template <>
void caf::AppEnum<RimWellEventPerf::State>::setUp()
void caf::AppEnum<RimWellEventEnums::State>::setUp()
{
addItem( RimWellEventPerf::State::OPEN, "OPEN", "Open" );
addItem( RimWellEventPerf::State::SHUT, "SHUT", "Shut" );
setDefault( RimWellEventPerf::State::OPEN );
addItem( RimWellEventEnums::State::OPEN, "OPEN", "Open" );
addItem( RimWellEventEnums::State::SHUT, "SHUT", "Shut" );
setDefault( RimWellEventEnums::State::OPEN );
}
} // namespace caf
@@ -18,6 +18,8 @@
#pragma once
#include "RimWellEventEnums.h"
#include "RimWellEvent.h"
#include "cafAppEnum.h"
@@ -36,11 +38,7 @@ class RimWellEventPerf : public RimWellEvent
CAF_PDM_HEADER_INIT;
public:
enum class State
{
OPEN,
SHUT
};
using State = RimWellEventEnums::State;
RimWellEventPerf();
~RimWellEventPerf() override;
@@ -26,14 +26,6 @@
namespace caf
{
template <>
void caf::AppEnum<RimWellEventValve::State>::setUp()
{
addItem( RimWellEventValve::State::OPEN, "OPEN", "Open" );
addItem( RimWellEventValve::State::SHUT, "SHUT", "Shut" );
setDefault( RimWellEventValve::State::OPEN );
}
template <>
void caf::AppEnum<RimWellEventValve::ValveType>::setUp()
{
@@ -18,6 +18,8 @@
#pragma once
#include "RimWellEventEnums.h"
#include "RimWellEvent.h"
#include "cafAppEnum.h"
@@ -36,11 +38,7 @@ class RimWellEventValve : public RimWellEvent
CAF_PDM_HEADER_INIT;
public:
enum class State
{
OPEN,
SHUT
};
using State = RimWellEventEnums::State;
enum class ValveType
{
@@ -162,7 +162,10 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
std::map<QString, EnumClassDef> enumDefByDataTypeName;
std::map<QString, QString> enumClassNameByDataTypeName;
std::map<QString, std::pair<QString, QString>> inferredEnumNameAndFieldByDataTypeName;
std::map<QString, std::pair<QString, QString>> enumTypeAndFieldByBaseName;
std::set<QString> ambiguousEnumDataTypesReported;
std::set<QString> duplicateEnumClassNamesReported;
bool duplicateEnumClassNameDetected = false;
// Seed with names that are already bound at module scope in the generated file (imports
// and module-level functions) so a generated enum cannot shadow them.
std::set<QString> usedEnumClassNames = { "PdmObjectBase",
@@ -242,6 +245,27 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
baseName = it->second.first;
}
QString fieldName = QString( "%1.%2" ).arg( field->ownerClass() ).arg( scriptability->scriptFieldName() );
auto [enumNameIt, inserted] =
enumTypeAndFieldByBaseName.emplace( baseName, std::make_pair( dataTypeName, fieldName ) );
if ( !inserted && enumNameIt->second.first != dataTypeName )
{
duplicateEnumClassNameDetected = true;
if ( !duplicateEnumClassNamesReported.count( baseName ) )
{
errorMessages.push_back( QString( "Duplicate Python enum class name '%1' is inferred for distinct enum "
"types used by fields '%2' and '%3'. "
"Register distinct names with caf::PdmScriptEnumNameRegistry, or "
"move the enum to a shared header if both fields "
"represent the same enum." )
.arg( baseName )
.arg( enumNameIt->second.second )
.arg( fieldName ) );
duplicateEnumClassNamesReported.insert( baseName );
}
return QString();
}
auto cached = enumClassNameByDataTypeName.find( dataTypeName );
if ( cached != enumClassNameByDataTypeName.end() ) return cached->second;
@@ -783,6 +807,7 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
}
}
if ( duplicateEnumClassNameDetected ) return QString();
return generatedCode;
}
@@ -23,6 +23,18 @@ void AppEnum<MyEnum>::setUp()
addItem( MyEnum::T6, "T6", "T6" );
setDefault( MyEnum::T1 );
}
template <>
void AppEnum<CollisionTestEnum>::setUp()
{
addItem( CollisionTestEnum::T1, "T1", "T1" );
addItem( CollisionTestEnum::T2, "T2", "T2" );
addItem( CollisionTestEnum::T3, "T3", "T3" );
addItem( CollisionTestEnum::T4, "T4", "T4" );
addItem( CollisionTestEnum::T5, "T5", "T5" );
addItem( CollisionTestEnum::T6, "T6", "T6" );
setDefault( CollisionTestEnum::T1 );
}
} // namespace caf
CAF_PDM_SOURCE_INIT( SimpleObj, "SimpleObj" );
@@ -172,6 +184,24 @@ void InheritedDemoObj::setUseAmbiguousEnumField( bool useAmbiguousEnumField )
s_useAmbiguousEnumField = useAmbiguousEnumField;
}
CAF_PDM_SOURCE_INIT( CollisionTestPdmObject, "CollisionTestPdmObject" );
bool CollisionTestPdmObject::s_useCollisionEnumField = false;
CollisionTestPdmObject::CollisionTestPdmObject()
{
CAF_PDM_InitScriptableObject( "Collision Test Object" );
if ( s_useCollisionEnumField )
{
CAF_PDM_InitScriptableFieldNoDefault( &m_enumField, "MyAppEnumValue", "A duplicate enum class name" );
}
}
void CollisionTestPdmObject::setUseCollisionEnumField( bool useCollisionEnumField )
{
s_useCollisionEnumField = useCollisionEnumField;
}
CAF_PDM_SOURCE_INIT( MyPdmDocument, "MyPdmDocument" );
//--------------------------------------------------------------------------------------------------
@@ -33,6 +33,16 @@ enum class MyEnum
T6
};
enum class CollisionTestEnum
{
T1,
T2,
T3,
T4,
T5,
T6
};
// This is a specialized class that can be used to implement custom behavior in configureCapabilities
// It is required to have a specialization of PdmFieldScriptingCapability for this class, as the new class
// MyAppEnumField is not known to the template
@@ -135,6 +145,20 @@ private:
static bool s_useAmbiguousEnumField;
};
class CollisionTestPdmObject : public caf::PdmObject
{
CAF_PDM_HEADER_INIT;
public:
CollisionTestPdmObject();
static void setUseCollisionEnumField( bool useCollisionEnumField );
caf::PdmField<caf::AppEnum<CollisionTestEnum>> m_enumField;
private:
static bool s_useCollisionEnumField;
};
class MyPdmDocument : public caf::PdmDocument
{
CAF_PDM_HEADER_INIT;
@@ -213,3 +213,23 @@ TEST( PdmPythonGenerator, ReportsAmbiguousEnumClassNames )
EXPECT_TRUE( errorMessages.front().contains( "AlternativeEnumValue" ) );
EXPECT_TRUE( errorMessages.front().contains( "PdmScriptEnumNameRegistry" ) );
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
TEST( PdmPythonGenerator, RejectsDuplicateEnumClassNames )
{
CollisionTestPdmObject::setUseCollisionEnumField( true );
caf::PdmPythonGenerator generator;
std::vector<QString> errorMessages;
QString generatedCode = generator.generate( caf::PdmDefaultObjectFactory::instance(), errorMessages );
CollisionTestPdmObject::setUseCollisionEnumField( false );
ASSERT_EQ( 1u, errorMessages.size() );
EXPECT_TRUE( generatedCode.isEmpty() );
EXPECT_TRUE( errorMessages.front().contains( "Duplicate Python enum class name 'MyAppEnumValue'" ) );
EXPECT_TRUE( errorMessages.front().contains( "InheritedDemoObj.MyAppEnumValue" ) );
EXPECT_TRUE( errorMessages.front().contains( "CollisionTestPdmObject.MyAppEnumValue" ) );
EXPECT_TRUE( errorMessages.front().contains( "shared header" ) );
}