From fa6aed4062af8763ad4c29bd067b96f244f8920d Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Fri, 1 Dec 2023 00:18:43 +0100
Subject: [PATCH] allow only one class fwd decl block

---
 Device/Data/Datafield.h                      |  8 ++--
 Fit/Minimizer/Types.h                        |  1 -
 GUI/Model/Job/MinimizerItem.h                | 11 +++---
 GUI/View/FitControl/FitObjectiveBuilder.h    |  9 ++---
 GUI/View/Instrument/InstrumentsTreeModel.cpp |  1 +
 GUI/View/Main/MainWindow.cpp                 |  1 -
 GUI/View/Main/MainWindow.h                   |  4 +-
 Sample/Correlations/Profiles2D.h             |  2 +-
 Sample/Lattice/Lattice3D.h                   |  1 -
 Sample/Material/IMaterialImpl.cpp            |  1 +
 Sample/Material/MaterialUtil.cpp             |  1 +
 Sample/Multilayer/RoughnessModels.h          |  1 +
 Sample/StandardSamples/ParacrystalBuilder.h  |  3 --
 Sim/Fitting/FitTypes.h                       |  1 -
 Sim/Fitting/PyFittingCallbacks.h             |  3 +-
 devtools/checks/check-scg-cpp-style.py       | 39 +++++++++++++++++---
 16 files changed, 53 insertions(+), 34 deletions(-)

diff --git a/Device/Data/Datafield.h b/Device/Data/Datafield.h
index a6f4de732c6..4002317cd00 100644
--- a/Device/Data/Datafield.h
+++ b/Device/Data/Datafield.h
@@ -15,14 +15,14 @@
 #ifndef BORNAGAIN_DEVICE_DATA_DATAFIELD_H
 #define BORNAGAIN_DEVICE_DATA_DATAFIELD_H
 
-#ifdef BORNAGAIN_PYTHON
-#include "PyCore/Embed/PyObjectDecl.h"
-#endif // BORNAGAIN_PYTHON
-
 #include <memory>
 #include <string>
 #include <vector>
 
+#ifdef BORNAGAIN_PYTHON
+#include "PyCore/Embed/PyObjectDecl.h"
+#endif // BORNAGAIN_PYTHON
+
 using std::size_t;
 
 class Frame;
diff --git a/Fit/Minimizer/Types.h b/Fit/Minimizer/Types.h
index 9fd0118809a..f7302a18715 100644
--- a/Fit/Minimizer/Types.h
+++ b/Fit/Minimizer/Types.h
@@ -28,7 +28,6 @@ class Parameters;
 using scalar_function_t = std::function<double(const std::vector<double>&)>;
 using gradient_function_t =
     std::function<double(const std::vector<double>&, unsigned int, std::vector<double>&)>;
-
 using root_scalar_t = std::function<double(const double*)>;
 using root_gradient_t = std::function<double(const double*, unsigned int, double*)>;
 using fcn_scalar_t = std::function<double(const mumufit::Parameters&)>;
diff --git a/GUI/Model/Job/MinimizerItem.h b/GUI/Model/Job/MinimizerItem.h
index 6f6c2c85fab..e9436e07c55 100644
--- a/GUI/Model/Job/MinimizerItem.h
+++ b/GUI/Model/Job/MinimizerItem.h
@@ -19,6 +19,11 @@
 
 class IMinimizer;
 class ObjectiveMetric;
+class GSLLMAMinimizerItem;
+class GSLMultiMinimizerItem;
+class GeneticMinimizerItem;
+class MinuitMinimizerItem;
+class SimAnMinimizerItem;
 
 enum class MinimizerType { Minuit2, GSLMultiMin, GSLLMA, GSLSimAn, Genetic };
 
@@ -39,12 +44,6 @@ public:
 
 //! Holds collection of minimizers and allows switching between them.
 
-class GSLLMAMinimizerItem;
-class GSLMultiMinimizerItem;
-class GeneticMinimizerItem;
-class MinuitMinimizerItem;
-class SimAnMinimizerItem;
-
 class MinimizerContainerItem : public MinimizerItem {
 public:
     MinimizerContainerItem();
diff --git a/GUI/View/FitControl/FitObjectiveBuilder.h b/GUI/View/FitControl/FitObjectiveBuilder.h
index 41049e37a9b..9f0eb319993 100644
--- a/GUI/View/FitControl/FitObjectiveBuilder.h
+++ b/GUI/View/FitControl/FitObjectiveBuilder.h
@@ -17,7 +17,11 @@
 
 #include <memory>
 
+class Datafield;
 class FitObjective;
+class GUIFitObserver;
+class IChiSquaredModule;
+class IMinimizer;
 class ISimulation;
 class JobItem;
 
@@ -25,11 +29,6 @@ namespace mumufit {
 class Parameters;
 }
 
-class Datafield;
-class GUIFitObserver;
-class IChiSquaredModule;
-class IMinimizer;
-
 class FitObjectiveBuilder {
 public:
     FitObjectiveBuilder(JobItem* jobItem);
diff --git a/GUI/View/Instrument/InstrumentsTreeModel.cpp b/GUI/View/Instrument/InstrumentsTreeModel.cpp
index bb7df5c9a1e..d714399d369 100644
--- a/GUI/View/Instrument/InstrumentsTreeModel.cpp
+++ b/GUI/View/Instrument/InstrumentsTreeModel.cpp
@@ -22,6 +22,7 @@
 namespace {
 
 using IType = InstrumentsTreeModel::InstrumentType;
+
 const auto types = {IType::Gisas, IType::Offspec, IType::Specular, IType::Depthprobe};
 
 } // namespace
diff --git a/GUI/View/Main/MainWindow.cpp b/GUI/View/Main/MainWindow.cpp
index c070ba1558f..da479aa7fe1 100644
--- a/GUI/View/Main/MainWindow.cpp
+++ b/GUI/View/Main/MainWindow.cpp
@@ -27,7 +27,6 @@
 #include "GUI/View/Widget/ApplicationSettings.h"
 #include <QAction>
 #include <QApplication>
-#include <QBoxLayout>
 #include <QCloseEvent>
 #include <QDir>
 #include <QMessageBox>
diff --git a/GUI/View/Main/MainWindow.h b/GUI/View/Main/MainWindow.h
index 635c181ddb4..2d92c7bc485 100644
--- a/GUI/View/Main/MainWindow.h
+++ b/GUI/View/Main/MainWindow.h
@@ -22,6 +22,7 @@
 #include <QStackedLayout>
 #include <QStatusBar>
 #include <QToolButton>
+#include <QVBoxLayout>
 
 class ActionManager;
 class DataView;
@@ -32,9 +33,6 @@ class ProjectsView;
 class SampleView;
 class SimulationView;
 
-
-class QVBoxLayout;
-
 class MainWindow : public QMainWindow {
     Q_OBJECT
 public:
diff --git a/Sample/Correlations/Profiles2D.h b/Sample/Correlations/Profiles2D.h
index ac15be25c8c..8b7219b3778 100644
--- a/Sample/Correlations/Profiles2D.h
+++ b/Sample/Correlations/Profiles2D.h
@@ -19,11 +19,11 @@
 #include "Param/Node/INode.h"
 #include <numbers>
 
-using std::numbers::pi;
 #ifndef SWIG
 #include "Sample/Correlations/IDistribution2DSampler.h"
 #endif // SWIG
 
+using std::numbers::pi;
 
 //! Interface for two-dimensional distributions in Fourier space.
 
diff --git a/Sample/Lattice/Lattice3D.h b/Sample/Lattice/Lattice3D.h
index a6cf668f589..02855443e0f 100644
--- a/Sample/Lattice/Lattice3D.h
+++ b/Sample/Lattice/Lattice3D.h
@@ -25,7 +25,6 @@ class ISelectionRule;
 
 //! A Bravais lattice, characterized by three basis vectors, and optionally an ISelectionRule.
 
-
 class Lattice3D : public INode {
 public:
     Lattice3D(R3 a, R3 b, R3 c);
diff --git a/Sample/Material/IMaterialImpl.cpp b/Sample/Material/IMaterialImpl.cpp
index bd91c353712..64e742924df 100644
--- a/Sample/Material/IMaterialImpl.cpp
+++ b/Sample/Material/IMaterialImpl.cpp
@@ -24,6 +24,7 @@ namespace {
 using PhysConsts::gamma_n;
 using PhysConsts::mu_B;
 using PhysConsts::r_e;
+
 // The factor 1e-18 is here to have unit: m/A*nm^-2
 constexpr double magnetization_prefactor = (gamma_n * r_e / 2.0 / mu_B) * 1e-18;
 
diff --git a/Sample/Material/MaterialUtil.cpp b/Sample/Material/MaterialUtil.cpp
index 71ce5592bd9..6171512e0ce 100644
--- a/Sample/Material/MaterialUtil.cpp
+++ b/Sample/Material/MaterialUtil.cpp
@@ -23,6 +23,7 @@ using PhysConsts::g_factor_n;
 using PhysConsts::h_bar;
 using PhysConsts::m_n;
 using PhysConsts::mu_N;
+
 // The factor 1e-18 is here to have unit: 1/T*nm^-2
 constexpr double magnetic_prefactor = (m_n * g_factor_n * mu_N / h_bar / h_bar) * 1e-18;
 
diff --git a/Sample/Multilayer/RoughnessModels.h b/Sample/Multilayer/RoughnessModels.h
index ff630c0560a..5ef30e68a4b 100644
--- a/Sample/Multilayer/RoughnessModels.h
+++ b/Sample/Multilayer/RoughnessModels.h
@@ -26,6 +26,7 @@ struct RoughnessModelWrap {
     static std::string roughnessModelName(RoughnessModel model);
 #endif // SWIG
 };
+
 using RoughnessModel = RoughnessModelWrap::RoughnessModel;
 
 #endif // BORNAGAIN_SAMPLE_MULTILAYER_ROUGHNESSMODELS_H
diff --git a/Sample/StandardSamples/ParacrystalBuilder.h b/Sample/StandardSamples/ParacrystalBuilder.h
index c3b3f61d590..35cc2a031cc 100644
--- a/Sample/StandardSamples/ParacrystalBuilder.h
+++ b/Sample/StandardSamples/ParacrystalBuilder.h
@@ -19,9 +19,6 @@
 #define BORNAGAIN_SAMPLE_STANDARDSAMPLES_PARACRYSTALBUILDER_H
 
 class MultiLayer;
-
-#include <memory>
-
 class IProfile2D;
 class Profile2DComponents;
 
diff --git a/Sim/Fitting/FitTypes.h b/Sim/Fitting/FitTypes.h
index 846d48abc52..ee57705b0bb 100644
--- a/Sim/Fitting/FitTypes.h
+++ b/Sim/Fitting/FitTypes.h
@@ -30,7 +30,6 @@ class Parameters;
 
 using simulation_builder_t =
     std::function<std::unique_ptr<ISimulation>(const mumufit::Parameters&)>;
-
 using fit_observer_t = std::function<void(const FitObjective&)>;
 
 #endif // BORNAGAIN_SIM_FITTING_FITTYPES_H
diff --git a/Sim/Fitting/PyFittingCallbacks.h b/Sim/Fitting/PyFittingCallbacks.h
index f404c1a80f1..1b762f6a516 100644
--- a/Sim/Fitting/PyFittingCallbacks.h
+++ b/Sim/Fitting/PyFittingCallbacks.h
@@ -19,6 +19,7 @@
 
 #include "Fit/Param/Parameters.h"
 
+class FitObjective;
 class ISimulation;
 
 //! Builds simulation object using a Python callable.
@@ -33,8 +34,6 @@ public:
     virtual ISimulation* build_simulation(const mumufit::Parameters&) const;
 };
 
-class FitObjective;
-
 //! Observer for FitObjective based on Python callable.
 //! Base class to wrap Python callable and pass it to C++. Used in swig interface file,
 //! intended to be overloaded from Python.
diff --git a/devtools/checks/check-scg-cpp-style.py b/devtools/checks/check-scg-cpp-style.py
index 4d7468bbc84..a3b05de5869 100755
--- a/devtools/checks/check-scg-cpp-style.py
+++ b/devtools/checks/check-scg-cpp-style.py
@@ -59,18 +59,44 @@ def check_parallel_includes(stem, th, tc, errs):
 
     errs.append(f"duplicate includes in source pair {stem}.h|cpp: {', '.join(duplicates)}")
 
-def check_block(fn, t, starter, errs):
+def rm_namespace(t):
+    return re.sub(r'\nnamespace ([\w:]+ )?{\n(.*\n)*?}.*', r'\nnamespace /*...*/', t)
 
-    matches = [m for m in re.finditer(r'(\n+)('+starter+r' .*?\n)+(\n+)', t)]
+def check_block(fn, t, name, errs):
+
+    if name == "include":
+        rex = r'(#include) .*?'
+    elif name == "using":
+        rex = r'(using) [^;]*?;'
+        t = rm_namespace(t)
+    elif name == "fwd decl":
+        rex = r'(class|struct) \w+;'
+        t = rm_namespace(t)
+    matches = [m for m in re.finditer(r'(\n*)\n('+ rex + r'([ ]*//.*)?\n)+(\n*)', t)]
+
+    if len(matches) > 1:
+
+        # tolerate multiple #include blocks if they are separated by some #if construct
+        if name == "include" and re.search('#include.*#if.*#include', t, re.DOTALL):
+            return
+
+        errs.append(f"several {name} blocks in file {fn}")
 
-    if len(matches) <= 1:
         return
 
-    if starter == "#include" and re.search('#include.*#if.*#include', t, re.DOTALL):
+    elif len(matches) == 0:
         return
 
-    errs.append(f"several {starter} blocks in file {fn}")
+    m = matches[0]
 
+    if m.group(1) == '':
+        errs.append(f"missing blank line above {name} block in file {fn}")
+    elif m.group(1) != '\n':
+        errs.append(f"more than one blank line above {name} block in file {fn}")
+    if m.group(5) == '':
+        errs.append(f"missing blank line below {name} block in file {fn}")
+    elif m.group(5) != '\n':
+        errs.append(f"more than one blank line below {name} block in file {fn}")
 
 ####################################################################################################
 # main
@@ -90,8 +116,9 @@ if __name__ == "__main__":
     for fn in allfiles:
         with open(fn, 'r') as fd:
             t = fd.read()
-        check_block(fn, t, '#include', errs)
+        check_block(fn, t, 'include', errs)
         check_block(fn, t, 'using', errs)
+        check_block(fn, t, 'fwd decl', errs)
 
     # tests on file pairs:
     for fn in allfiles:
-- 
GitLab