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