diff --git a/CHANGES.current b/CHANGES.current index f0c044c76..2f6185581 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,11 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.1.0 (in progress) =========================== +2022-02-26: wsfulton + #655 #1840 Add new warning WARN_LANG_USING_NAME_DIFFERENT to warn when a + method introduced by a using declaration in a derived class cannot + be used due to a conflict in names. + 2022-02-24: olly #1465 An invalid preprocessor expression is reported as a pair of warnings with the second giving a more detailed message from the diff --git a/Doc/Manual/SWIGPlus.html b/Doc/Manual/SWIGPlus.html index d480f61f3..9820a1800 100644 --- a/Doc/Manual/SWIGPlus.html +++ b/Doc/Manual/SWIGPlus.html @@ -1125,7 +1125,7 @@ customization features.

SWIG wraps class members that are public following the C++ conventions, i.e., by explicit public declaration or by the use of - the using directive. In general, anything specified in a + using declarations. In general, anything specified in a private or protected section will be ignored, although the internal code generator sometimes looks at the contents of the private and protected sections so that it can properly generate code for default @@ -2083,7 +2083,6 @@ or for statically typed languages like Java:

 example.i:4: Warning 516: Overloaded method foo(long) ignored,
 example.i:3: Warning 516: using foo(int) instead.
-at example.i:3 used.
 
@@ -2330,8 +2329,8 @@ also apply to %ignore. For example:
 %ignore foo(double);          // Ignore all foo(double)
-%ignore Spam::foo;            // Ignore foo in class Spam
-%ignore Spam::foo(double);    // Ignore foo(double) in class Spam
+%ignore Spam::foo;            // Ignore foo in class Spam (and foo in any derived classes)
+%ignore Spam::foo(double);    // Ignore foo(double) in class Spam (and foo in any derived classes)
 %ignore *::foo(double);       // Ignore foo(double) in all classes
 
@@ -2386,6 +2385,53 @@ global scope (e.g., a renaming of Spam::foo takes precedence over a renaming of foo(int)).

+
  • +Renaming a class member, using an unparameterized but qualified name, such as Spam::foo, also applies to members in all derived classes +that have members with the same name. +This can be used to simply rename a method, across an entire class hierarchy for all overloaded and non-overloaded methods. +This also applies to methods introduced via using declarations, see +Using declarations and inheritance. +For example: + +

    +
    +%rename(foo_new) Spam::foo;
    +
    +class Spam {
    +public:
    +  virtual void foo(int);      // Renamed to foo_new
    +};
    +
    +class Bar : public Spam {
    +public:
    +  virtual void foo(int);      // Renamed to foo_new
    +  void foo(bool, short, int); // Renamed to foo_new
    +};
    +
    +class Grok : public Bar {
    +public:
    +  virtual void foo(int);      // Renamed to foo_new
    +  void foo(bool, int);        // Renamed to foo_new
    +  void foo(const char *);     // Renamed to foo_new
    +  void foo(Bar *);            // Renamed to foo_new
    +};
    +
    +class Spok : public Grok {
    +public:
    +  void foo();                 // Renamed to foo_new
    +};
    +
    +class Knock : public Spok {
    +public:
    +  using Grok::foo;            // Introduced methods renamed to foo_new
    +};
    +
    +
    + +

    +
  • + +
  • The order in which %rename directives are defined does not matter as long as they appear before the declarations to be renamed. Thus, there is no difference @@ -4073,23 +4119,23 @@ math::Complex c;

    At this level, namespaces are relatively easy to manage. However, things start to get very ugly when you throw in the other ways a namespace can be used. For example, -selective symbols can be exported from a namespace with using. +selective symbols can be exported from a namespace with a using declaration:

    -using math::Complex;
    +using math::Complex;                // Using declaration
     double magnitude(Complex *c);       // Namespace prefix stripped
     

    -Similarly, the contents of an entire namespace can be made available like this: +Similarly, the contents of an entire namespace can be made available via a using directive:

    -using namespace math;
    +using namespace math;               // Using directive
     double x = sin(1.0);
     double magnitude(Complex *c);
     
    @@ -4246,9 +4292,11 @@ Similarly, %ignore can be used to ignore declarations.

    -using declarations do not have any effect on the generated wrapper -code. They are ignored by SWIG language modules and they do not result in any -code. However, these declarations are used by the internal type +C++ using directives and using declarations +do not add any code to the generated wrapper code. +However, there is an exception in one context, see Using declarations and inheritance +for introducing members of a base class into a derived class definition. +C++ using declarations and directives are used by the internal type system to track type-names. Therefore, if you have code like this:

    @@ -5172,7 +5220,7 @@ exit # 'a' is released, SWIG unref 'a' called in the destructor wra

    -using declarations are sometimes used to adjust access to members of +C++ using declarations are sometimes used to introduce members of base classes. For example:

    @@ -5180,7 +5228,7 @@ base classes. For example:
     class Foo {
     public:
    -  int  blah(int x);
    +  int blah(int x);
     };
     
     class Bar {
    @@ -5228,7 +5276,8 @@ you wrap this code in Python, the module works just like you would expect:
     

    -using declarations can also be used to change access when applicable. For example: +C++ using declarations can also be used to change access when applicable. +For example, protected methods in a base class can be made public in a derived class:

    @@ -5261,15 +5310,15 @@ ignored in a base class, it will also be ignored by a using declaration

    Because a using declaration does not provide fine-grained -control over the declarations that get imported, it may be difficult +control over the declarations that get imported, because a single using declaration +may introduce multiple methods, it may be difficult to manage such declarations in applications that make heavy use of SWIG customization features. If you can't get using to work -correctly, you can always change the interface to the following: +correctly, you can always modify the C++ code to handle SWIG differently such as:

    -
     class FooBar : public Foo, public Bar {
     public:
     #ifndef SWIG
    @@ -5285,13 +5334,36 @@ public:
     
    +

    +If the C++ code being wrapped cannot be changed, make judicious usage of %extend and %rename +to ignore and unignore declarations. The example below is effectively the same as above: +

    + +
    +
    +%extend FooBar {
    +  int blah(int x) { return $self->Foo::blah(x); }
    +  double blah(double x) { return $self->Bar::blah(x); }
    +}
    +%ignore FooBar::blah; // ignore all FooBar::blah below
    +%rename("") FooBar::blah(const char *x); // parameterized unignore
    +
    +class FooBar : public Foo, public Bar {
    +public:
    +  using Foo::blah;
    +  using Bar::blah;
    +  char *blah(const char *x);
    +};
    +
    +
    +

    Notes:

    6.27 Nested classes

    diff --git a/Doc/Manual/Warnings.html b/Doc/Manual/Warnings.html index 5d4d3773b..bb20db38b 100644 --- a/Doc/Manual/Warnings.html +++ b/Doc/Manual/Warnings.html @@ -537,6 +537,7 @@ example.i(4) : Syntax error in input(1).
  • 523. Use of an illegal destructor name 'name' in %extend is deprecated, the destructor name should be 'name'.
  • 524. Experimental target language. Target language language specified by lang is an experimental language. Please read about SWIG experimental languages, htmllink.
  • 525. Destructor declaration is final, name cannot be a director class. +
  • 526. Using declaration declaration, with name 'name', is not actually using the method from declaration, with name 'name', as the names are different.

    19.9.6 Doxygen comments (560-599)

    diff --git a/Examples/test-suite/errors/cpp_using_rename.stderr b/Examples/test-suite/errors/cpp_using_rename.stderr new file mode 100644 index 000000000..546489655 --- /dev/null +++ b/Examples/test-suite/errors/cpp_using_rename.stderr @@ -0,0 +1,7 @@ +cpp_using_rename.i:18: Warning 526: Using declaration Base::use_me, with name 'use_me', is not actually using +cpp_using_rename.i:10: Warning 526: the method from Base::use_me(int), with name 'UseMe', as the names are different. +cpp_using_rename.i:19: Warning 526: Using declaration Base::use_me_too, with name 'UseMeToo', is not actually using +cpp_using_rename.i:11: Warning 526: the method from Base::use_me_too(double) const, with name 'use_me_too', as the names are different. +cpp_using_rename.i:19: Warning 526: Using declaration Base::use_me_too, with name 'UseMeToo', is not actually using +cpp_using_rename.i:12: Warning 526: the method from Base::use_me_too(bool) const, with name 'use_me_too', as the names are different. +cpp_using_rename.i:20: Warning 315: Nothing known about 'Base::does_not_exist'. diff --git a/Examples/test-suite/python/using_member_runme.py b/Examples/test-suite/python/using_member_runme.py index eda5b5043..a0ea64888 100644 --- a/Examples/test-suite/python/using_member_runme.py +++ b/Examples/test-suite/python/using_member_runme.py @@ -12,3 +12,13 @@ bb = BB() swig_assert_equal(bb.greater(int(1)), 0) swig_assert_equal(bb.greater(float(1)), 1) swig_assert_equal(bb.great(True), 2) + +cc = CC() +swig_assert_equal(cc.greater(int(10)), 0) +swig_assert_equal(cc.greater(float(10)), 1) +swig_assert_equal(cc.greater(True), 20) + +dd = DD() +swig_assert_equal(dd.greater(int(10)), 0) +swig_assert_equal(dd.greater(float(10)), 1) +swig_assert_equal(dd.greaterstill(True), 30) diff --git a/Examples/test-suite/using_member.i b/Examples/test-suite/using_member.i index 3d00622c4..6b4af2ea7 100644 --- a/Examples/test-suite/using_member.i +++ b/Examples/test-suite/using_member.i @@ -1,7 +1,13 @@ %module using_member +/* using declaration tests, including renaming */ +%warnfilter(SWIGWARN_LANG_USING_NAME_DIFFERENT) one::twotwo::threetwo::BB::great; + %rename(greater) one::two::three::interface1::AA::great(int); %rename(greater) one::two::three::interface1::AA::great(float); +%rename(greater) one::CC::great; +%rename(greater) one::DD::great; +%rename(greaterstill) one::DD::great(bool); %inline %{ namespace interface1 @@ -46,5 +52,19 @@ namespace one { }; } } + + class CC : public two::three::AA + { + public: + using two::three::AA::great; + int great(bool) {return 20;} + }; + + class DD : public two::three::AA + { + public: + using two::three::AA::great; + int great(bool) {return 30;} + }; } %} diff --git a/Source/Include/swigwarn.h b/Source/Include/swigwarn.h index a57afbe36..751f1df28 100644 --- a/Source/Include/swigwarn.h +++ b/Source/Include/swigwarn.h @@ -213,6 +213,7 @@ #define WARN_LANG_EXTEND_DESTRUCTOR 523 #define WARN_LANG_EXPERIMENTAL 524 #define WARN_LANG_DIRECTOR_FINAL 525 +#define WARN_LANG_USING_NAME_DIFFERENT 526 /* -- Doxygen comments -- */ diff --git a/Source/Modules/typepass.cxx b/Source/Modules/typepass.cxx index dc84cf94a..086e7c3bf 100644 --- a/Source/Modules/typepass.cxx +++ b/Source/Modules/typepass.cxx @@ -1047,13 +1047,6 @@ class TypePass:private Dispatcher { || (Getattr(c, "feature:extend") && !Getattr(c, "code")) || GetFlag(c, "feature:ignore"))) { - /* Don't generate a method if the method is overridden in this class, - * for example don't generate another m(bool) should there be a Base::m(bool) : - * struct Derived : Base { - * void m(bool); - * using Base::m; - * }; - */ String *csymname = Getattr(c, "sym:name"); if (!csymname || (Strcmp(csymname, symname) == 0)) { { @@ -1069,6 +1062,13 @@ class TypePass:private Dispatcher { over = Getattr(over, "sym:nextSibling"); } if (match) { + /* Don't generate a method if the method is overridden in this class, + * for example don't generate another m(bool) should there be a Base::m(bool) : + * struct Derived : Base { + * void m(bool); + * using Base::m; + * }; + */ c = Getattr(c, "csym:nextSibling"); continue; } @@ -1117,6 +1117,9 @@ class TypePass:private Dispatcher { } else { Delete(nn); } + } else { + Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(n), Getline(n), "Using declaration %s, with name '%s', is not actually using\n", SwigType_namestr(Getattr(n, "uname")), symname); + Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(c), Getline(c), "the method from %s, with name '%s', as the names are different.\n", Swig_name_decl(c), csymname); } } }