7 # --------------------------------------------------------------------------------------------
9 # --------------------------------------------------------------------------------------------
11 definitionSet
= set() # set of tuple(return_type, name_and_params)
12 definitionToSourceLocationMap
= dict()
13 calledFromDict
= dict()
14 calledFromOutsideSet
= set()
15 largeFunctionSet
= set() # set of tuple(return_type, name_and_params)
16 addressOfSet
= set() # set of tuple(return_type, name_and_params)
18 # clang does not always use exactly the same numbers in the type-parameter vars it generates
19 # so I need to substitute them to ensure we can match correctly.
20 normalizeTypeParamsRegex
= re
.compile(r
"type-parameter-\d+-\d+")
21 def normalizeTypeParams( line
):
22 return normalizeTypeParamsRegex
.sub("type-parameter-?-?", line
)
24 # --------------------------------------------------------------------------------------------
26 # --------------------------------------------------------------------------------------------
28 with io
.open("workdir/loplugin.expandablemethods.log", "rb", buffering
=1024*1024) as txt
:
30 tokens
= line
.strip().split("\t")
31 if tokens
[0] == "definition:":
33 returnType
= tokens
[2]
34 nameAndParams
= tokens
[3]
35 sourceLocation
= tokens
[4]
36 funcInfo
= (normalizeTypeParams(returnType
), normalizeTypeParams(nameAndParams
))
37 definitionSet
.add(funcInfo
)
38 definitionToSourceLocationMap
[funcInfo
] = sourceLocation
39 elif tokens
[0] == "calledFrom:":
40 calleeLocation
= tokens
[1]
41 returnType
= tokens
[2]
42 nameAndParams
= tokens
[3]
43 funcInfo
= (normalizeTypeParams(returnType
), normalizeTypeParams(nameAndParams
))
44 if not funcInfo
in calledFromDict
:
45 calledFromDict
[funcInfo
] = set()
46 calledFromDict
[funcInfo
].add(calleeLocation
)
47 elif tokens
[0] == "outside:":
48 returnType
= tokens
[1]
49 nameAndParams
= tokens
[2]
50 calledFromOutsideSet
.add((normalizeTypeParams(returnType
), normalizeTypeParams(nameAndParams
)))
51 elif tokens
[0] == "large:":
52 returnType
= tokens
[1]
53 nameAndParams
= tokens
[2]
54 largeFunctionSet
.add((normalizeTypeParams(returnType
), normalizeTypeParams(nameAndParams
)))
55 elif tokens
[0] == "addrof:":
56 returnType
= tokens
[1]
57 nameAndParams
= tokens
[2]
58 addressOfSet
.add((normalizeTypeParams(returnType
), normalizeTypeParams(nameAndParams
)))
60 print( "unknown line: " + line
)
62 # Invert the definitionToSourceLocationMap.
63 # If we see more than one method at the same sourceLocation, it's being autogenerated as part of a template
64 # and we should just ignore it.
65 sourceLocationToDefinitionMap
= {}
66 for k
, v
in definitionToSourceLocationMap
.iteritems():
67 sourceLocationToDefinitionMap
[v
] = sourceLocationToDefinitionMap
.get(v
, [])
68 sourceLocationToDefinitionMap
[v
].append(k
)
69 for k
, definitions
in sourceLocationToDefinitionMap
.iteritems():
70 if len(definitions
) > 1:
72 definitionSet
.remove(d
)
74 def isOtherConstness( d
, callSet
):
75 method
= d
[0] + " " + d
[1]
76 # if this method is const, and there is a non-const variant of it, and the non-const variant is in use, then leave it alone
77 if d
[0].startswith("const ") and d
[1].endswith(" const"):
78 if ((d
[0][6:],d
[1][:-6]) in callSet
):
80 elif method
.endswith(" const"):
81 method2
= method
[:len(method
)-6] # strip off " const"
82 if ((d
[0],method2
) in callSet
):
84 if method
.endswith(" const") and ("::iterator" in method
):
85 method2
= method
[:len(method
)-6] # strip off " const"
86 method2
= method2
.replace("::const_iterator", "::iterator")
87 if ((d
[0],method2
) in callSet
):
89 # if this method is non-const, and there is a const variant of it, and the const variant is in use, then leave it alone
90 if (not method
.endswith(" const")) and ((d
[0],"const " + method
+ " const") in callSet
):
92 if (not method
.endswith(" const")) and ("::iterator" in method
):
93 method2
= method
.replace("::iterator", "::const_iterator") + " const"
94 if ((d
[0],method2
) in callSet
):
98 # sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely
99 def natural_sort_key(s
, _nsre
=re
.compile('([0-9]+)')):
100 return [int(text
) if text
.isdigit() else text
.lower()
101 for text
in re
.split(_nsre
, s
)]
102 def sort_set_by_natural_key(s
):
103 return sorted(s
, key
=lambda v
: natural_sort_key(v
[1]))
106 # --------------------------------------------------------------------------------------------
107 # Methods that are only called from inside their own class, and are only called from one spot
108 # --------------------------------------------------------------------------------------------
111 for d
in definitionSet
:
112 if d
in calledFromOutsideSet
:
114 if isOtherConstness(d
, calledFromOutsideSet
):
116 if d
not in definitionToSourceLocationMap
:
117 print("warning, method has no location: " + d
[0] + " " + d
[1])
119 # ignore external code
120 if definitionToSourceLocationMap
[d
].startswith("external/"):
122 # ignore constructors, calledFromOutsideSet does not provide accurate info for them
123 tokens
= d
[1].split("(")[0].split("::")
124 if len(tokens
)>1 and tokens
[-2] == tokens
[-1]:
126 # ignore large methods, which make the code clearer by being out of line
127 if d
in largeFunctionSet
:
129 # ignore methods whose address we take
130 if d
in addressOfSet
:
132 # ignore unused methods, leave them to the dedicated analysis
133 if d
not in calledFromDict
:
135 # ignore methods called from more than one site
136 if len(calledFromDict
[d
]) > 1:
139 method
= d
[0] + " " + d
[1]
140 tmp4set
.add((method
, definitionToSourceLocationMap
[d
]))
142 # print output, sorted by name and line number
143 with
open("loplugin.expandablemethods.report", "wt") as f
:
144 for t
in sort_set_by_natural_key(tmp4set
):
146 f
.write(" " + t
[0] + "\n")