Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++ final keyword in method definition #3952

Open
guerinp38 opened this issue Mar 12, 2024 · 15 comments
Open

C++ final keyword in method definition #3952

guerinp38 opened this issue Mar 12, 2024 · 15 comments

Comments

@guerinp38
Copy link

(
The final C++ keyword is handled at class level as well as inlined methods in the class ; it appears as a property.
In my case, I use ctags for extracting definitions from source files to inject them in classes for declarations.
The override keyword is handled as a property but not final.

Is it possible to add it ?

Kind regards,
Patrice.
)


The name of the parser: C++

The command line you used to run ctags:

$ ctags --extras=+f -n --kinds-C++=cnsuf --fields=zKZStse --fields-C++={properties} -u -D OVERRIDE=override -D FINAL=final

The content of input file:

PUBLIC off_t	PIFC::FILE_IO::LSeek( IN off_t Pos, IN int From ) FINAL OVERRIDE
{
return lseek( ( MyFd, Pos, From );
}

The tags output you are not satisfied with:

LSeek	H:/Src/protoc/executio/file_io.cpp	200;"	kind:function	scope:class:PIFC::FILE_IO	typeref:typename:PUBLIC off_t	signature:(IN off_t Pos,IN int From)	end:215	properties:override,virtual

The tags output you expect:

LSeek	H:/Src/protoc/executio/file_io.cpp	200;"	kind:function	scope:class:PIFC::FILE_IO	typeref:typename:PUBLIC off_t	signature:(IN off_t Pos,IN int From)	end:215	properties:final,override,virtual

The version of ctags:

$ ctags --version
Universal Ctags 6.1.0, Copyright (C) 2015-2023 Universal Ctags Team
Universal Ctags is derived from Exuberant Ctags.
Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert
  Compiled: Mar 12 2024, 16:27:40
  URL: https://ctags.io/
  Output version: 0.0
  Optional compiled features: +win32, +wildcards, +regex, +internal-sort, +unix-path-separator, +option-directory, +case-insensitive-filenames, +optscript

How do you get ctags binary:

(
Building it locally, Microsoft Visual C++ 2013, from github

$ git log -1
commit 38fd8e3 (HEAD -> master, tag: p6.1.20240310.0, origin/master, origin/HEAD)
Merge: 45f200c c433018
Author: Masatake YAMATO yamato@redhat.com
Date: Sat Mar 9 10:15:16 2024 +0900

)

@masatake
Copy link
Member

Thank you for reporting.
Could you show an example input that a C++ compiler accepts?
I don't know C++ well.

I read https://en.cppreference.com/w/cpp/language/final.

It seems that the final keyword can be used in class or struct definition.

PUBLIC off_t	PIFC::FILE_IO::LSeek( IN off_t Pos, IN int From ) FINAL OVERRIDE
{
return lseek( ( MyFd, Pos, From );
}

This method definition is at the top level. Is this correct C++ code?
Could you show me another example input that does not use PUBLIC, off_t, PIFC, FILE_IO, and IN?

@masatake
Copy link
Member

If the final keyword is used inside a struct, it is recorded in properties: field as expected.

[yamato@dev64]/tmp% cat foo.cpp     
struct Base
{
    virtual void foo();
};
 
struct A : Base
{
    void foo() final; // Base::foo is overridden and A::foo is the final override
};
 
struct B final : A // struct B is final
{
};
[yamato@dev64]/tmp% ctags --fields-C++='+{properties}' --kinds-C++=+p -o - foo.cpp     
A       foo.cpp /^struct A : Base$/;"   s       file:
B       foo.cpp /^struct B final : A \/\/ struct B is final$/;" s       file:
Base    foo.cpp /^struct Base$/;"       s       file:
foo     foo.cpp /^    virtual void foo();$/;"   p       struct:Base     typeref:typename:void   file:   properties:virtual
foo     foo.cpp /^    void foo() final; \/\/ Base::foo is overridden and A::foo is the final override$/;"       p       struct:A        typeref:typename:void   file:   properties:final,virtual
[yamato@dev64]/tmp% ctags --version                                               
Universal Ctags 6.0.0, Copyright (C) 2015-2022 Universal Ctags Team
Universal Ctags is derived from Exuberant Ctags.
Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert
  Compiled: Jul 19 2023, 00:00:00
  URL: https://ctags.io/
  Output version: 0.0
  Optional compiled features: +wildcards, +regex, +iconv, +option-directory, +xpath, +json, +interactive, +sandbox, +yaml, +packcc, +optscript

@guerinp38
Copy link
Author

Hello,

Thank for your fast response.

I didn't include any include file or further explanations, so here they are

FILE_IO is a class that is defined in another class PIFC
PUBLIC, IN are empty macros that serve as indicators
PUBLIC the method is to be a public method
IN indicates that the parameter is an input parameter
off_t is the standard type for the lseek function return value
FINAL and OVERRIDE are also empty macros that serve as indicators too, to express final and/or override method

My prototyping program uses the ctags outputs from source files and include files
to inject method definitions in the correct class, visibilty and properties
So I use the slicing into kind, scope, typeref, signature and properties to build the method declaration
and inject it into the include file
PUBLIC is removed from the typeref and replaced by public:

At compilation stage, as PUBLIC, IN, FINAL and OVERRIDE are empty macros, the source file compiles successfully

foo.cpp

include "pifc.h"
PUBLIC off_t	PIFC::FILE_IO::LSeek( IN off_t Pos, IN int From ) FINAL OVERRIDE
{
return lseek( ( MyFd, Pos, From );
}

pifc.h

# define PUBLIC
# define IN
# define FINAL
# define OVERRIDE

class PIFC
{
  class FILE_IO
  {
    public:
      off_t LSeek( IN off_t Pos, IN int From ) final override; //injected from the source file definition
    ...
    };
 ...
 };

Kind regards,
Patrice.

@masatake
Copy link
Member

I'm not sure I understand what you want.

As a principle of u-ctags design, ctags works only for valid input.

If you pass -DFINAL=final -DOVERRIDE=override, ctags sees:

PUBLIC off_t	PIFC::FILE_IO::LSeek( IN off_t Pos, IN int From ) final override
{
return lseek( ( MyFd, Pos, From );
}

as input.

As an input of C++ language, using the final and override keywords with a method implementation is not allowed as far as my C++ knowledge. (Fix me if I'm incorrect.) For such an invalid input, the behavior of ctags is predictable. Give me time. I will show my idea.

@guerinp38
Copy link
Author

Hello Masatake,

Cppreference.com (https://en.cppreference.com/w/cpp/language/final) exhibits

declarator virt-specifier-seq (optional) function-body

As I explained before, my exemple works with OVERRIDE and ctags shows the override property.
So why final is not ?

class example
{
public:
	bool foo() final override	{ return true; };
};

should show

foo	H:/Src/protoc/executio/local.h	18;"	kind:function	scope:class:example	typeref:typename:bool	signature:()	end:18	properties:final,override,virtual

BUT, in fact, you are right, final and override keywords are ONLY allowed in class definitions
the following exemple doesn't compile with g++
However when I use FINAL and OVERRIDE macros, it compiles successfully.

//example.cpp
class foo
{
public:
	virtual bool foo2();
};
class example : public foo
{
public:
	virtual bool foo1() final;
	bool foo2() override;
};

bool example::foo1() final
{
	return true;
	}
bool example::foo2() override
{
	return true;
	}
guerinp@spock12:~/src$ g++ --std=c++11 example.cpp
example.cpp:13:22: error: virt-specifiers in ‘foo1’ not allowed outside a class definition
   13 | bool example::foo1() final
      |                      ^~~~~
example.cpp:17:22: error: virt-specifiers in ‘foo2’ not allowed outside a class definition
   17 | bool example::foo2() override
      |                      ^~~~~~~~

@guerinp38
Copy link
Author

Hello again,

I think I have a clue
I rewrite example.cpp like this

class foo
{
};
class example : public foo
{
};

bool example::foo1() final
{
	return true;
	}
bool example::foo2() override
{
	return true;
	}
bool example::foo3() FINAL
{
	return true;
	}
bool example::foo4() OVERRIDE
{
	return true;
	}
PUBLIC bool example::foo5( IN int test ) FINAL OVERRIDE
{
	return true;
	}

to ease the process, consider the following options file

# sources.ctags
--extras=+f
-n
--kinds-C++=cnsuf
--fields=zKZStse
--fields-C++={properties}
-u
-D OVERRIDE=override
-D FINAL=final

Running

ctags --options=sources.ctags example.cpp

the ctags output for methods is

foo	example.cpp	1;"	kind:class	end:3
example	example.cpp	4;"	kind:class	end:6
foo1	example.cpp	8;"	kind:function	scope:class:example	typeref:typename:bool	signature:()	end:11	properties:final,virtual
foo2	example.cpp	12;"	kind:function	scope:class:example	typeref:typename:bool	signature:()	end:15	properties:override,virtual
foo3	example.cpp	16;"	kind:function	scope:class:example	typeref:typename:bool	signature:()	end:19	properties:final,virtual
foo4	example.cpp	20;"	kind:function	scope:class:example	typeref:typename:bool	signature:()	end:23	properties:override,virtual
foo5	example.cpp	24;"	kind:function	scope:class:example	typeref:typename:PUBLIC bool	signature:(IN int test)	end:27	properties:final,override,virtual
example.cpp	example.cpp	1;"	kind:file	end:27

the final and override properties are shown.

If I commented out, classes definitions at the top
the output changes to

foo1	example.cpp	9;"	kind:function	scope:class:example	typeref:typename:bool	signature:()	end:12
foo2	example.cpp	13;"	kind:function	scope:class:example	typeref:typename:bool	signature:()	end:16	properties:override,virtual
foo3	example.cpp	17;"	kind:function	scope:class:example	typeref:typename:bool	signature:()	end:20
foo4	example.cpp	21;"	kind:function	scope:class:example	typeref:typename:bool	signature:()	end:24	properties:override,virtual
foo5	example.cpp	25;"	kind:function	scope:class:example	typeref:typename:PUBLIC bool	signature:(IN int test)	end:28	properties:override,virtual
example.cpp	example.cpp	1;"	kind:file	end:28

The final property doesn't appear. Only the override property is shown.

Kind regards,
Patrice.

@masatake
Copy link
Member

As I explained before, my exemple works with OVERRIDE and ctags shows the override property.
So why final is not ?

This looks bug. As you wrote:

BUT, in fact, you are right, final and override keywords are ONLY allowed in class definitions

It seems that not only the final keyword but also the override keyword should be rejected.

@guerinp38
Copy link
Author

Hello again,

That's OK for me.

Have a nice day

Kind regards,
Patrice.

@masatake
Copy link
Member

Give me time. I will show my idea.

Adding final to the properties: field is not a good idea as I wrote.
If you want to attach final to the tag, you should define your own field for the purpose.

$ cat /tmp/foo.cpp 
#include "pifc.h"
PUBLIC off_t    PIFC::FILE_IO::LSeek( IN off_t Pos, IN int From ) FINAL OVERRIDE
{
return lseek( MyFd, Pos, From );
}
$ ~/bin/ctags --options=NONE -o - --options=mytool.ctags foo.cpp
ctags: Notice: No options will be read from files or environment
LSeek   foo.cpp /^PUBLIC off_t  PIFC::FILE_IO::LSeek( IN off_t Pos, IN int From ) FINAL OVERRIDE$/;"    f       class:PIFC::FILE_IO     typeref:typename:PUBLIC off_t   myprops:final,override

In this example, I defined myprops: field.
I modified the original input a bit; I deleted an extra (). This triggers a critical bug in ctags.

mytool.ctags:

-DFINAL=
-DOVERRIDE=

--_fielddef-C++=myprops,extra properties{datatype=string}
--fields-C++=+{myprops}

--regex-C++=/(\))[[:space:]]((FINAL|OVERRIDE|[[:space:]])+)$//{postrun}{{
   @1 _intervaltab {
      dup :kind /function eq {
         % function-index
         mark
            \2 (FINAL) _strstr {
               pop pop (final)
            } if
            \2 (OVERRIDE) _strstr {
               pop pop (override)
            } if
	 % function-index mark ...
	 counttomark 2 eq {
	    (,) exch
	 } if
	 counttomark 0 eq {
	    % function-index mark
	    pop pop
	 } {
	    % function-index mark ...
	    _buildstring C++.myprops:
	 } ifelse
      } {
         % Not a function
         pop
      } ifelse
   } if
}}

@masatake
Copy link
Member

masatake commented Mar 13, 2024

   @1 _intervaltab {
      dup :kind /function eq {

@1 represents the start position of the 1st group of the regex pattern.
_intervaltab is for an operator accessing the interval table; Look up a tag
including the position @1. If the kind of the tag is a function...

@guerinp38
Copy link
Author

Wahou, very impressive !

What language is this ? it's a sort of mix between FORTH and postscript...
Is there documentation anywhere ?
I've seen extending ctags with regex parser (optlib)

@masatake
Copy link
Member

I call it optscript mostly derived from postscript.
See https://ctags.io/2021/01/05/optscript/ .

It is not documented yet and is immature.
While sketching the idea, I found two critical bugs. One is fixed in #3953. However, another may take longer to fix.
In addition, some commits I have on my PC are needed. The commits implement foreign fields.

@masatake
Copy link
Member

You are interested in

ctags --_list-operators

@masatake
Copy link
Member

I remembered I wrote about optscript a bit: https://docs.ctags.io/en/latest/optscript.html

@guerinp38
Copy link
Author

Thank you for these informations, I will take a look.

Kind regards,
Patrice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants