1#!/usr/local/bin/python3.8
2
3import sys
4
5instantiatedSet = set()
6definitionSet = set()
7parentChildDict = {}
8definitionToFileDict = {}
9
10with open("workdir/loplugin.mergeclasses.log") as txt:
11    for line in txt:
12        tokens = line.strip().split("\t")
13
14        if len(tokens) == 1:
15            pass
16
17        elif tokens[0] == "instantiated:":
18            clazzName = tokens[1]
19            if (clazzName.startswith("const ")):
20                clazzName = clazzName[6:]
21            if (clazzName.startswith("class ")):
22                clazzName = clazzName[6:]
23            if (clazzName.startswith("::")):
24                clazzName = clazzName[2:]
25            instantiatedSet.add(clazzName)
26
27        elif tokens[0] == "definition:":
28            clazzName = tokens[1]
29            # the 1.. is so we skip the leading /
30            fileName  = tokens[2][1:]
31            definitionSet.add(clazzName)
32            definitionToFileDict[clazzName] = fileName
33
34        elif tokens[0] == "has-subclass:":
35            child  = tokens[1]
36            parent = tokens[2]
37            if (parent.startswith("class ")):
38                parent = parent[6:]
39            elif (parent.startswith("struct ")):
40                parent = parent[7:]
41            if (child.startswith("class ")):
42                child = child[6:]
43            elif (child.startswith("struct ")):
44                child = child[7:]
45            if (parent not in parentChildDict):
46                parentChildDict[parent] = set()
47            parentChildDict[parent].add(child)
48
49def extractModuleName(clazz):
50    filename = definitionToFileDict[clazz]
51    if filename.startswith("include/"):
52        filename = filename[8:]
53    idx = filename.find("/")
54    return filename[:idx]
55
56with open("compilerplugins/clang/mergeclasses.results", "wt") as f:
57    # loop over defined, but not instantiated classes
58    for clazz in sorted(definitionSet - instantiatedSet):
59        if clazz == "svl::IUndoManager": print parentChildDict[clazz]
60        # ignore classes without any children, and classes with more than one child
61        if (clazz not in parentChildDict) or (len(parentChildDict[clazz]) != 1):
62            continue
63        # exclude some common false positives
64        a = ['Dialog', 'Dlg', 'com::sun']
65        if any(x in clazz for x in a):
66            continue
67        # ignore base class that contain the word "mutex", they are normally there to
68        # help with the WeakComponentImpl template magic
69        if ("mutex" in clazz) or ("Mutex" in clazz):
70            continue
71        otherclazz = next(iter(parentChildDict[clazz]))
72        if clazz == "svl::IUndoManager": print extractModuleName(clazz)
73        if otherclazz == "svl::IUndoManager": print extractModuleName(otherclazz)
74        # exclude combinations that span modules because we often use those to make cross-module dependencies more manageable.
75        if extractModuleName(clazz) != extractModuleName(otherclazz):
76            continue
77        f.write( "merge " + clazz + " with " + otherclazz + "\n" )
78
79