1#!/usr/local/bin/python3.8 2 3import sys 4import re 5import io 6 7definitionSet = set() 8protectedAndPublicDefinitionSet = set() # set of tuple(type, name) 9definitionToSourceLocationMap = dict() 10definitionToTypeMap = dict() 11touchedFromInsideSet = set() 12touchedFromOutsideSet = set() 13touchedFromOutsideConstructorSet = set() 14readFromSet = set() 15writeToSet = set() 16sourceLocationSet = set() 17 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. 20normalizeTypeParamsRegex = re.compile(r"type-parameter-\d+-\d+") 21def normalizeTypeParams( line ): 22 return normalizeTypeParamsRegex.sub("type-parameter-?-?", line) 23 24def parseFieldInfo( tokens ): 25 if len(tokens) == 3: 26 return (normalizeTypeParams(tokens[1]), tokens[2]) 27 else: 28 return (normalizeTypeParams(tokens[1]), "") 29 30with io.open("workdir/loplugin.unusedfields.log", "rb", buffering=1024*1024) as txt: 31 for line in txt: 32 tokens = line.strip().split("\t") 33 if tokens[0] == "definition:": 34 access = tokens[1] 35 fieldInfo = (normalizeTypeParams(tokens[2]), tokens[3]) 36 srcLoc = tokens[5] 37 # ignore external source code 38 if (srcLoc.startswith("external/")): 39 continue 40 # ignore build folder 41 if (srcLoc.startswith("workdir/")): 42 continue 43 definitionSet.add(fieldInfo) 44 definitionToTypeMap[fieldInfo] = tokens[4] 45 if access == "protected" or access == "public": 46 protectedAndPublicDefinitionSet.add(fieldInfo) 47 definitionToSourceLocationMap[fieldInfo] = tokens[5] 48 elif tokens[0] == "inside:": 49 touchedFromInsideSet.add(parseFieldInfo(tokens)) 50 elif tokens[0] == "outside:": 51 touchedFromOutsideSet.add(parseFieldInfo(tokens)) 52 elif tokens[0] == "outside-constructor:": 53 touchedFromOutsideConstructorSet.add(parseFieldInfo(tokens)) 54 elif tokens[0] == "read:": 55 readFromSet.add(parseFieldInfo(tokens)) 56 elif tokens[0] == "write:": 57 writeToSet.add(parseFieldInfo(tokens)) 58 else: 59 print( "unknown line: " + line) 60 61# Calculate untouched 62untouchedSet = set() 63untouchedSetD = set() 64for d in definitionSet: 65 if d in touchedFromOutsideSet or d in touchedFromInsideSet: 66 continue 67 srcLoc = definitionToSourceLocationMap[d]; 68 # this is all representations of on-disk data structures 69 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx") 70 or srcLoc.startswith("sw/source/filter/ww8/") 71 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx") 72 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx") 73 or srcLoc.startswith("vcl/inc/unx/XIM.h") 74 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h") 75 or srcLoc.startswith("include/svl/svdde.hxx") 76 or srcLoc.startswith("lotuswordpro/source/filter/lwpsdwdrawheader.hxx") 77 or srcLoc.startswith("hwpfilter/") 78 or srcLoc.startswith("embeddedobj/source/inc/") 79 or srcLoc.startswith("svtools/source/dialogs/insdlg.cxx") 80 or srcLoc.startswith("bridges/")): 81 continue 82 if d[0] in set([ "AtkObjectWrapperClass", "AtkObjectWrapper", "GLOMenu", "GLOAction", "_XRegion", "SalMenuButtonItem", "Vertex", 83 "OOoMountOperationClass", "SwCSS1ItemIds", "ScCompiler::AddInMap", "MemoryByteGrabber", "textcat_t", "fp_t", "ngram_t", 84 "ImplPPTParaPropSet", "DataNode"]): 85 continue 86 # unit testing code 87 if srcLoc.startswith("cppu/source/uno/check.cxx"): 88 continue 89 fieldType = definitionToTypeMap[d] 90 if "ModuleClient" in fieldType: 91 continue 92 # leave the weld stuff alone until Caolan is finished 93 if "weld::" in fieldType: 94 continue 95 if "::sfx2::sidebar::ControllerItem" in fieldType: 96 continue 97 untouchedSet.add((d[0] + " " + d[1] + " " + fieldType, srcLoc)) 98 untouchedSetD.add(d) 99 100# Calculate only-touched-in-constructor set 101onlyUsedInConstructorSet = set() 102for d in definitionSet: 103 if d in touchedFromOutsideSet or d in touchedFromOutsideConstructorSet: 104 continue 105 srcLoc = definitionToSourceLocationMap[d]; 106 # this is all representations of on-disk data structures 107 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx") 108 or srcLoc.startswith("sw/source/filter/ww8/") 109 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx") 110 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx") 111 or srcLoc.startswith("vcl/inc/unx/XIM.h") 112 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h") 113 or srcLoc.startswith("include/svl/svdde.hxx") 114 or srcLoc.startswith("lotuswordpro/source/filter/lwpsdwdrawheader.hxx") 115 or srcLoc.startswith("hwpfilter/") 116 or srcLoc.startswith("embeddedobj/source/inc/") 117 or srcLoc.startswith("svtools/source/dialogs/insdlg.cxx") 118 or srcLoc.startswith("bridges/")): 119 continue 120 fieldType = definitionToTypeMap[d] 121 if "std::unique_ptr" in fieldType: 122 continue 123 if "std::shared_ptr" in fieldType: 124 continue 125 if "Reference<" in fieldType: 126 continue 127 if "VclPtr<" in fieldType: 128 continue 129 # leave the weld stuff alone until Caolan is finished 130 if "weld::" in fieldType: 131 continue 132 if "osl::Mutex" in fieldType: 133 continue 134 if "::sfx2::sidebar::ControllerItem" in fieldType: 135 continue 136 onlyUsedInConstructorSet.add((d[0] + " " + d[1] + " " + fieldType, srcLoc)) 137 138writeonlySet = set() 139for d in definitionSet: 140 parentClazz = d[0]; 141 if d in readFromSet or d in untouchedSetD: 142 continue 143 srcLoc = definitionToSourceLocationMap[d]; 144 # this is all representations of on-disk data structures 145 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx") 146 or srcLoc.startswith("sw/source/filter/ww8/") 147 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx") 148 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx") 149 or srcLoc.startswith("vcl/inc/unx/XIM.h") 150 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h") 151 or srcLoc.startswith("include/svl/svdde.hxx") 152 or srcLoc.startswith("lotuswordpro/source/filter/lwpsdwdrawheader.hxx") 153 or srcLoc.startswith("svtools/source/dialogs/insdlg.cxx")): 154 continue 155 fieldType = definitionToTypeMap[d] 156 if "ModuleClient" in fieldType: 157 continue 158 if "::sfx2::sidebar::ControllerItem" in fieldType: 159 continue 160 # ignore reference fields, because writing to them actually writes to another field somewhere else 161 if fieldType.endswith("&"): 162 continue 163 # ignore the import/export data model stuff 164 if srcLoc.startswith("sc/source/filter/inc/") and "Model" in fieldType: 165 continue 166 if srcLoc.startswith("sc/source/filter/inc/") and (parentClazz.startswith("Xcl") or parentClazz.startswith("oox::xls::")): 167 continue 168 # implement some kind of registration of errors 169 if fieldType == "class SfxErrorHandler *": 170 continue 171 # mutex locking 172 if "Guard" in fieldType: 173 continue 174 # leave the weld stuff alone until Caolan is finished 175 if "weld::" in fieldType: 176 continue 177 # these are just all model classes 178 if (srcLoc.startswith("oox/") 179 or srcLoc.startswith("lotuswordpro/") 180 or srcLoc.startswith("include/oox/") 181 or srcLoc.startswith("include/filter/") 182 or srcLoc.startswith("hwpfilter/") 183 or srcLoc.startswith("filter/")): 184 continue 185 186 writeonlySet.add((d[0] + " " + d[1] + " " + definitionToTypeMap[d], srcLoc)) 187 188 189readonlySet = set() 190for d in definitionSet: 191 parentClazz = d[0]; 192 if d in writeToSet or d in untouchedSetD: 193 continue 194 fieldType = definitionToTypeMap[d] 195 srcLoc = definitionToSourceLocationMap[d]; 196 if "ModuleClient" in fieldType: 197 continue 198 # this is all representations of on-disk data structures 199 if (srcLoc.startswith("sc/source/filter/inc/scflt.hxx") 200 or srcLoc.startswith("sw/source/filter/ww8/") 201 or srcLoc.startswith("vcl/source/filter/sgvmain.hxx") 202 or srcLoc.startswith("vcl/source/filter/sgfbram.hxx") 203 or srcLoc.startswith("vcl/inc/unx/XIM.h") 204 or srcLoc.startswith("vcl/inc/unx/gtk/gloactiongroup.h") 205 or srcLoc.startswith("include/svl/svdde.hxx")): 206 continue 207 # I really don't care about these ancient file formats 208 if (srcLoc.startswith("hwpfilter/") 209 or srcLoc.startswith("lotuswordpro/")): 210 continue 211 # leave the weld stuff alone until Caolan is finished 212 if "weld::" in fieldType: 213 continue 214 readonlySet.add((d[0] + " " + d[1] + " " + definitionToTypeMap[d], srcLoc)) 215 216 217canBePrivateSet = set() 218for d in protectedAndPublicDefinitionSet: 219 clazz = d[0] + " " + d[1] 220 if d in touchedFromOutsideSet: 221 continue 222 srcLoc = definitionToSourceLocationMap[d]; 223 224 canBePrivateSet.add((clazz + " " + definitionToTypeMap[d], srcLoc)) 225 226 227# -------------------------------------------------------------------------------------------- 228# "all fields in class can be made private" analysis 229# -------------------------------------------------------------------------------------------- 230 231potentialClasses = set() 232excludedClasses = set() 233potentialClassesSourceLocationMap = dict() 234matchClassName = re.compile(r"(\w+)::") 235for d in protectedAndPublicDefinitionSet: 236 clazz = d[0] 237 if d in touchedFromOutsideSet: 238 excludedClasses.add(clazz) 239 else: 240 potentialClasses.add(clazz) 241 potentialClassesSourceLocationMap[clazz] = definitionToSourceLocationMap[d] 242allFieldsCanBePrivateSet = set() 243for d in (potentialClasses - excludedClasses): 244 sourceLoc = potentialClassesSourceLocationMap[d] 245 # when the class is inside a compile unit, assume that the compiler can figure this out for itself, much less interesting to me 246 if not ".cxx" in sourceLoc: 247 allFieldsCanBePrivateSet.add((d, sourceLoc)) 248 249# sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely 250def natural_sort_key(s, _nsre=re.compile('([0-9]+)')): 251 return [int(text) if text.isdigit() else text.lower() 252 for text in re.split(_nsre, s)] 253 254# sort results by name and line number 255tmp1list = sorted(untouchedSet, key=lambda v: natural_sort_key(v[1])) 256tmp2list = sorted(writeonlySet, key=lambda v: natural_sort_key(v[1])) 257tmp3list = sorted(canBePrivateSet, key=lambda v: natural_sort_key(v[1])) 258tmp4list = sorted(readonlySet, key=lambda v: natural_sort_key(v[1])) 259tmp5list = sorted(onlyUsedInConstructorSet, key=lambda v: natural_sort_key(v[1])) 260tmp6list = sorted(allFieldsCanBePrivateSet, key=lambda v: natural_sort_key(v[1])) 261 262# print out the results 263with open("compilerplugins/clang/unusedfields.untouched.results", "wt") as f: 264 for t in tmp1list: 265 f.write( t[1] + "\n" ) 266 f.write( " " + t[0] + "\n" ) 267with open("compilerplugins/clang/unusedfields.writeonly.results", "wt") as f: 268 for t in tmp2list: 269 f.write( t[1] + "\n" ) 270 f.write( " " + t[0] + "\n" ) 271# this one is not checked in yet because I haven't actually done anything with it 272with open("loplugin.unusedfields.report-can-be-private", "wt") as f: 273 for t in tmp3list: 274 f.write( t[1] + "\n" ) 275 f.write( " " + t[0] + "\n" ) 276with open("compilerplugins/clang/unusedfields.readonly.results", "wt") as f: 277 for t in tmp4list: 278 f.write( t[1] + "\n" ) 279 f.write( " " + t[0] + "\n" ) 280with open("compilerplugins/clang/unusedfields.only-used-in-constructor.results", "wt") as f: 281 for t in tmp5list: 282 f.write( t[1] + "\n" ) 283 f.write( " " + t[0] + "\n" ) 284with open("compilerplugins/clang/unusedfields.report-all-can-be-private", "wt") as f: 285 for t in tmp6list: 286 f.write( t[1] + "\n" ) 287 f.write( " " + t[0] + "\n" ) 288 289 290