File: fix_unused_public.py

package info (click to toggle)
cp2k 6.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 204,532 kB
  • sloc: fortran: 835,196; f90: 59,605; python: 9,861; sh: 7,882; cpp: 4,868; ansic: 2,807; xml: 2,185; lisp: 733; pascal: 612; perl: 547; makefile: 497; csh: 16
file content (154 lines) | stat: -rwxr-xr-x 4,680 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# author: Ole Schuett

import re, sys
from os import path
from os.path import dirname, basename, normpath
import os

# pre-compiled regular expressions
re_module  = re.compile(r"(?:^|\n)\s*module\s+(\w+)\s.*\n\s*end\s*module",re.DOTALL)
re_useonly = re.compile(r"\n\s*use\s+(\w+)\s*,\s*only\s*:(.*)(?=\n)")
re_use     = re.compile(r"\n\s*use\s+(\w+)\s*(?=\n)")
re_pub     = re.compile(r"\n\s*public\s*::\s*(.*)(?=\n)")


#=============================================================================
def main():
    src_dir = "../src"
    parsed_files = {}
    for root, dir, files in os.walk(src_dir):
        if("preprettify" in root):
            continue
        if(".svn" in root):
            continue
        for fn in files:
            if(not fn.endswith(".F")):
                continue
            absfn = root+"/"+fn

            parsed_files[absfn] =  parse_file(absfn)

    all_used_symboles = set()
    for p in parsed_files.values():
        for m, syms in p['use']:
            for s in syms:
                all_used_symboles.add(m+"@"+s)

    n = 0
    for fn, p in parsed_files.items():
        if(len(p['mod']) != 1):
            continue
        m = p['mod'][0]
        if(m+"@*" in all_used_symboles):
            continue
        unused = []
        for s in p['pub']:
            if(m+"@"+s not in all_used_symboles):
                unused.append(s)
                n += 1
        if(len(unused) > 0):
            #print("%s USElessly declares PUBLIC: "%fn+ ", ".join(unused)+"\n")
            clean_publics(fn, unused)

    #print("Found %d unUSEd PUBLIC symbols."%n)


#=============================================================================
def clean_publics(fn, unused):
    content = open(fn).read()
    new_content = ""
    active = False
    protected = False
    new_public = []
    for line in content.split("\n"):
        if(line.strip().startswith("!API")):
            protected = True
        if(re.match(r"\s*public\s*::.*", line, re.IGNORECASE)):
            if(protected):
                protected = False
            else:
                active = True

        if(not active):
            new_content += line + "\n"
            continue

        prefix, symbols, comment = re.match("^(.*::)?([^!]*)(!.*)?$", line).groups()
        old_symbols = symbols.strip(" &,").split(",")
        new_symbols = []
        for s in old_symbols:
            s = s.strip()
            if(s.lower() not in unused):
                new_symbols.append(s)

        if(len(new_symbols) > 0):
            new_public.append( (", ".join(new_symbols), comment) )

        without_comment = re.sub("!.*", "", line).strip()
        if(len(without_comment) > 0 and without_comment[-1] != "&"):

            #flush new_public
            for i, entry in enumerate(new_public):
                if(i==0):
                    new_content += "  PUBLIC :: "
                else:
                    new_content += "            "
                new_content += entry[0]
                if(i < len(new_public)-1):
                    new_content += ",&"
                if(entry[1]):
                    new_content += "  " + entry[1]
                new_content += "\n"

            active = False
            new_public = []

    new_content = new_content[:-1] # remove last line break

    if(new_content != content):
        print("Fixed: ", fn)
        f = open(fn, "w")
        f.write(new_content)


#=============================================================================
def parse_file(fn):
    # print("parsing "+fn)
    content = open(fn).read()
    # re.IGNORECASE is horribly expensive. Converting to lower-case upfront
    content = content.lower()
    content = re.sub("!.*\n", "\n", content)
    content = re.sub("&\s*\n", "", content)
    content = re.sub("&", " ", content)

    mods = re_module.findall(content)

    uses = []
    matches = re_use.findall(content)
    for m in matches:
        uses.append((m.strip(), ("*",)))
        if(m.strip() not in ("iso_c_binding", "f77_blas", )):
            print("missing ONLY-clause: ", fn, m)

    matches = re_useonly.findall(content)
    for m in matches:
        syms = [p.split("=>")[-1].strip() for p in m[1].split(",")]
        uses.append((m[0].strip(), syms))

    publics = []
    matches = re_pub.findall(content)
    for m in matches:
        publics += [p.strip() for p in m.split(",")]

    return({"mod":mods, "use":uses, "pub":publics})

#===============================================================================
if(len(sys.argv)==2 and sys.argv[-1]=="--selftest"):
    pass #TODO implement selftest
else:
    main()

#EOF