Skip to content

Commit 7341933

Browse files
committed
Python: resolve real kinds for tags with unknown kinds when a hint file is given
As the first pass, make a tags file with --fields=+{language}. In the second pass, specified the tags file created in the first pass with --_hint-file=<tags file>. Signed-off-by: Masatake YAMATO <[email protected]>
1 parent b39f487 commit 7341933

File tree

8 files changed

+150
-7
lines changed

8 files changed

+150
-7
lines changed

Tmain/list-roles.d/stdout-expected.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,15 @@ Perl M/module unused on specified in `no' bu
5656
Perl M/module used on specified in `use' built-in function
5757
Protobuf D/protodef imported on imported
5858
Protobuf m/message extension on extending the message
59+
Python c/class imported on imported module/imported from the other module
60+
Python c/class indirectlyImported on classes/variables/functions/modules imported in alternative name
61+
Python f/function imported on imported module/imported from the other module
62+
Python f/function indirectlyImported on classes/variables/functions/modules imported in alternative name
5963
Python i/module imported on imported module/imported from the other module
6064
Python i/module indirectlyImported on classes/variables/functions/modules imported in alternative name
6165
Python i/module namespace on namespace from where classes/variables/functions are imported
66+
Python v/variable imported on imported module/imported from the other module
67+
Python v/variable indirectlyImported on classes/variables/functions/modules imported in alternative name
6268
Python x/unknown imported on imported module/imported from the other module
6369
Python x/unknown indirectlyImported on classes/variables/functions/modules imported in alternative name
6470
R l/library library on library attached by library function
@@ -142,9 +148,15 @@ Perl M/module unused on specified in `no' bu
142148
Perl M/module used on specified in `use' built-in function
143149
Protobuf D/protodef imported on imported
144150
Protobuf m/message extension on extending the message
151+
Python c/class imported on imported module/imported from the other module
152+
Python c/class indirectlyImported on classes/variables/functions/modules imported in alternative name
153+
Python f/function imported on imported module/imported from the other module
154+
Python f/function indirectlyImported on classes/variables/functions/modules imported in alternative name
145155
Python i/module imported on imported module/imported from the other module
146156
Python i/module indirectlyImported on classes/variables/functions/modules imported in alternative name
147157
Python i/module namespace on namespace from where classes/variables/functions are imported
158+
Python v/variable imported on imported module/imported from the other module
159+
Python v/variable indirectlyImported on classes/variables/functions/modules imported in alternative name
148160
Python x/unknown imported on imported module/imported from the other module
149161
Python x/unknown indirectlyImported on classes/variables/functions/modules imported in alternative name
150162
R l/library library on library attached by library function
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# This must be fixed
2+
--_hint-file=Units/parser-python.r/resolve-unknown-kind.d/hint.tags
3+
4+
--extras=+r
5+
--fields=+rEK
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
C0 input.py /^from mymod0 import C0$/;" class module:mymod0 roles:imported extras:reference
2+
f0 input.py /^from mymod0 import f0$/;" function module:mymod0 roles:imported extras:reference
3+
mymod0 input.py /^from mymod0 import C0$/;" module roles:namespace extras:reference
4+
mymod0 input.py /^from mymod0 import f0$/;" module roles:namespace extras:reference
5+
mymod0 input.py /^from mymod0 import v0$/;" module roles:namespace extras:reference
6+
v0 input.py /^from mymod0 import v0$/;" variable module:mymod0 roles:imported extras:reference
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# u-ctags -o hint.tags --options=hint.ctags mymod0.py
2+
--sort=no
3+
--fields=+{language}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
2+
!_TAG_FILE_SORTED 0 /0=unsorted, 1=sorted, 2=foldcase/
3+
!_TAG_PROGRAM_AUTHOR Universal Ctags Team //
4+
!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/
5+
!_TAG_PROGRAM_URL https://ctags.io/ /official site/
6+
!_TAG_PROGRAM_VERSION 5.9.0 /8ca48ff0/
7+
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
8+
!_TAG_OUTPUT_FILESEP slash /slash or backslash/
9+
!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
10+
!_TAG_PROC_CWD /home/jet/var/ctags/Units/parser-python.r/resolve-unknown-kind.d/ //
11+
!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
12+
C0 mymod0.py /^class C0:$/;" c language:Python
13+
f0 mymod0.py /^ def f0(self):$/;" m language:Python class:C0
14+
f0 mymod0.py /^def f0():$/;" f language:Python
15+
v0 mymod0.py /^v0 = 1$/;" v language:Python
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from mymod0 import C0
2+
from mymod0 import f0
3+
from mymod0 import v0
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class C0:
2+
def f0(self):
3+
pass
4+
5+
def f0():
6+
pass
7+
8+
v0 = 1

parsers/python.c

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
#include "debug.h"
2424
#include "xtag.h"
2525
#include "objpool.h"
26+
#include "hint.h"
27+
#include "routines.h"
28+
#include "field.h"
2629

2730
#define isIdentifierChar(c) \
2831
(isalnum (c) || (c) == '_' || (c) >= 0x80)
@@ -133,12 +136,18 @@ static roleDefinition PythonModuleRoles [] = {
133136
};
134137

135138
defineRolesWithCommoneElements (Unknown);
139+
defineRolesWithCommoneElements (Class);
140+
defineRolesWithCommoneElements (Variable);
141+
defineRolesWithCommoneElements (Function);
136142

137143
static kindDefinition PythonKinds[COUNT_KIND] = {
138-
{true, 'c', "class", "classes"},
139-
{true, 'f', "function", "functions"},
144+
{true, 'c', "class", "classes",
145+
.referenceOnly = false, ATTACH_ROLES(PythonClassRoles)},
146+
{true, 'f', "function", "functions",
147+
.referenceOnly = false, ATTACH_ROLES(PythonFunctionRoles)},
140148
{true, 'm', "member", "class members"},
141-
{true, 'v', "variable", "variables"},
149+
{true, 'v', "variable", "variables",
150+
.referenceOnly = false, ATTACH_ROLES(PythonVariableRoles)},
142151
{true, 'I', "namespace", "name referring a module defined in other file"},
143152
{true, 'i', "module", "modules",
144153
.referenceOnly = true, ATTACH_ROLES(PythonModuleRoles)},
@@ -1057,6 +1066,86 @@ static bool parseClassOrDef (tokenInfo *const token,
10571066
return true;
10581067
}
10591068

1069+
struct foreachHintData
1070+
{
1071+
const char *module_name;
1072+
size_t module_namelen;
1073+
int kind;
1074+
};
1075+
1076+
static bool langobjIsInTheModule (const char *name, hintEntry *hint, void *data)
1077+
{
1078+
struct foreachHintData *hdata = data;
1079+
1080+
const char *pname = getLanguageName (Lang_python);
1081+
Assert (pname);
1082+
const char *lang = hintFieldForType (hint, FIELD_LANGUAGE);
1083+
1084+
if (lang == NULL)
1085+
return true; /* continue the finding */
1086+
1087+
if (strcmp (lang, pname))
1088+
return true; /* continue the finding */
1089+
1090+
const char *f = strrstr (hint->file, hdata->module_name);
1091+
if (f == NULL)
1092+
return true; /* continue the finding */
1093+
1094+
if (hdata->module_namelen == 0)
1095+
hdata->module_namelen = strlen(hdata->module_name);
1096+
const char * suffix = f + hdata->module_namelen;
1097+
if (strcmp(suffix, ".py") != 0)
1098+
return true; /* continue the finding */
1099+
1100+
if (!(hint->kind && *hint->kind))
1101+
return true;
1102+
1103+
kindDefinition *kdef = NULL;
1104+
if (hint->kind[1] == '\0')
1105+
kdef = getLanguageKindForLetter (Lang_python, *hint->kind);
1106+
else
1107+
kdef = getLanguageKindForName (Lang_python, hint->kind);
1108+
1109+
if (kdef == NULL)
1110+
{
1111+
/* Not found; please continue the finding. */
1112+
return true;
1113+
}
1114+
1115+
switch (kdef->id)
1116+
{
1117+
case K_CLASS:
1118+
case K_FUNCTION:
1119+
case K_VARIABLE:
1120+
hdata->kind = kdef->id;
1121+
/* Found; please stop the finding. */
1122+
return false;
1123+
default:
1124+
/* Not found; please continue the finding. */
1125+
return true;
1126+
}
1127+
}
1128+
1129+
static int resolveKindWithHints (const char *name, int moduleIndex)
1130+
{
1131+
if (!isHintAvailable ())
1132+
return K_UNKNOWN;
1133+
1134+
tagEntryInfo *e = getEntryInCorkQueue (moduleIndex);
1135+
if (!e)
1136+
return K_UNKNOWN;
1137+
1138+
struct foreachHintData data = {
1139+
.kind = K_UNKNOWN,
1140+
.module_name = e->name,
1141+
.module_namelen = 0,
1142+
};
1143+
1144+
foreachHintEntries (name, TAG_FULLMATCH, langobjIsInTheModule, &data);
1145+
1146+
return data.kind;
1147+
}
1148+
10601149
static bool parseImport (tokenInfo *const token)
10611150
{
10621151
tokenInfo *fromModule = NULL;
@@ -1120,7 +1209,8 @@ static bool parseImport (tokenInfo *const token)
11201209
int index;
11211210

11221211
/* Y */
1123-
index = makeSimplePythonRefTag (name, NULL, K_UNKNOWN,
1212+
int kind = resolveKindWithHints (vStringValue (name->string), moduleIndex);
1213+
index = makeSimplePythonRefTag (name, NULL, kind,
11241214
PYTHON_COMMON_INDIRECTLY_IMPORTED,
11251215
XTAG_UNKNOWN);
11261216
/* fill the scope field for Y */
@@ -1129,11 +1219,11 @@ static bool parseImport (tokenInfo *const token)
11291219
e->extensionFields.scopeIndex = moduleIndex;
11301220

11311221
/* Z */
1132-
index = makeSimplePythonTag (token, K_UNKNOWN);
1222+
index = makeSimplePythonTag (token, kind);
11331223
/* fill the nameref filed for Y */
11341224
if (PythonFields[F_NAMEREF].enabled)
11351225
{
1136-
vString *nameref = vStringNewInit (PythonKinds [K_UNKNOWN].name);
1226+
vString *nameref = vStringNewInit (PythonKinds [kind].name);
11371227
vStringPut (nameref, ':');
11381228
vStringCat (nameref, name->string);
11391229
attachParserFieldToCorkEntry (index, PythonFields[F_NAMEREF].ftype,
@@ -1178,7 +1268,8 @@ static bool parseImport (tokenInfo *const token)
11781268
x = (kind:module, role:namespace),
11791269
Y = (kind:unknown, role:imported, scope:module:x) */
11801270
/* Y */
1181-
int index = makeSimplePythonRefTag (name, NULL, K_UNKNOWN,
1271+
int kind = resolveKindWithHints (vStringValue (name->string), moduleIndex);
1272+
int index = makeSimplePythonRefTag (name, NULL, kind,
11821273
PYTHON_COMMON_IMPORTED,
11831274
XTAG_UNKNOWN);
11841275
/* fill the scope field for Y */

0 commit comments

Comments
 (0)