diff options
author | Noel Grandin <noel.grandin@collabora.co.uk> | 2017-05-05 13:03:43 +0200 |
---|---|---|
committer | Noel Grandin <noel.grandin@collabora.co.uk> | 2017-05-08 08:29:03 +0200 |
commit | 185ed3ddb8c01ee4465ce559e37113824f57b5c7 (patch) | |
tree | 596455ca4b9dc85666efbf06a1e1e0a3eec3ee2d /compilerplugins | |
parent | d33e262a244f351febc9dbe605b05f76cb834eeb (diff) |
teach loplugin:constantparam about simple constructor calls
Change-Id: I7d2a28ab5951fbdb5a427c84e9ac4c1e32ecf9f9
Reviewed-on: https://gerrit.libreoffice.org/37280
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Diffstat (limited to 'compilerplugins')
-rw-r--r-- | compilerplugins/clang/constantparam.cxx | 22 | ||||
-rwxr-xr-x | compilerplugins/clang/constantparam.py | 69 |
2 files changed, 76 insertions, 15 deletions
diff --git a/compilerplugins/clang/constantparam.cxx b/compilerplugins/clang/constantparam.cxx index d748919b0c7d..3f22324c60c2 100644 --- a/compilerplugins/clang/constantparam.cxx +++ b/compilerplugins/clang/constantparam.cxx @@ -24,6 +24,9 @@ $ ./compilerplugins/clang/constantparam.py TODO look for OUString and OString params and check for call-params that are always either "" or default constructed + + FIXME this plugin manages to trigger crashes inside clang, when calling EvaluateAsInt, so I end up disabling it for a handful of files + here and there. */ namespace { @@ -172,7 +175,24 @@ std::string ConstantParam::getCallValue(const Expr* arg) return "defaultConstruct"; } } - return "unknown2"; + + // Get the expression contents. + // This helps us find params which are always initialised with something like "OUString()". + SourceManager& SM = compiler.getSourceManager(); + SourceLocation startLoc = arg->getLocStart(); + SourceLocation endLoc = arg->getLocEnd(); + const char *p1 = SM.getCharacterData( startLoc ); + const char *p2 = SM.getCharacterData( endLoc ); + if (!p1 || !p2 || (p2 - p1) < 0 || (p2 - p1) > 40) { + return "unknown"; + } + unsigned n = Lexer::MeasureTokenLength( endLoc, SM, compiler.getLangOpts()); + std::string s( p1, p2 - p1 + n); + // strip linefeed and tab characters so they don't interfere with the parsing of the log file + std::replace( s.begin(), s.end(), '\r', ' '); + std::replace( s.begin(), s.end(), '\n', ' '); + std::replace( s.begin(), s.end(), '\t', ' '); + return s; } bool ConstantParam::VisitCallExpr(const CallExpr * callExpr) { diff --git a/compilerplugins/clang/constantparam.py b/compilerplugins/clang/constantparam.py index 2dede4f1ad8d..fd299d85405b 100755 --- a/compilerplugins/clang/constantparam.py +++ b/compilerplugins/clang/constantparam.py @@ -15,19 +15,34 @@ def normalizeTypeParams( line ): # reading as binary (since we known it is pure ascii) is much faster than reading as unicode with io.open("loplugin.constantparam.log", "rb", buffering=1024*1024) as txt: for line in txt: - tokens = line.strip().split("\t") - returnType = normalizeTypeParams(tokens[0]) - nameAndParams = normalizeTypeParams(tokens[1]) - sourceLocation = tokens[2] - paramName = tokens[3] - paramType = normalizeTypeParams(tokens[4]) - callValue = tokens[5] - callInfo = (returnType, nameAndParams, paramName, paramType, sourceLocation) - if not callInfo in callDict: - callDict[callInfo] = set() - callDict[callInfo].add(callValue) + try: + tokens = line.strip().split("\t") + returnType = normalizeTypeParams(tokens[0]) + nameAndParams = normalizeTypeParams(tokens[1]) + sourceLocation = tokens[2] + paramName = tokens[3] + paramType = normalizeTypeParams(tokens[4]) + callValue = tokens[5] + callInfo = (returnType, nameAndParams, paramName, paramType, sourceLocation) + if not callInfo in callDict: + callDict[callInfo] = set() + callDict[callInfo].add(callValue) + except IndexError: + print "problem with line " + line.strip() + raise + +def RepresentsInt(s): + try: + int(s) + return True + except ValueError: + return False + +consRegex = re.compile("^\w+\(\)$") tmp1list = list() +tmp2list = list() +tmp3list = list() for callInfo, callValues in callDict.iteritems(): nameAndParams = callInfo[1] if len(callValues) != 1: @@ -51,21 +66,47 @@ for callInfo, callValues in callDict.iteritems(): # part of our binary API if sourceLoc.startswith("include/LibreOfficeKit"): continue - v2 = callInfo[3] + " " + callInfo[2] + " " + callValue - tmp1list.append((sourceLoc, functionSig, v2)) + if RepresentsInt(callValue): + if callValue == "0" or callValue == "1": + tmp1list.append((sourceLoc, functionSig, callInfo[3] + " " + callInfo[2], callValue)) + else: + tmp2list.append((sourceLoc, functionSig, callInfo[3] + " " + callInfo[2], callValue)) + # look for places where the callsite is always a constructor invocation + elif consRegex.match(callValue): + if callValue.startswith("Get"): continue + if callValue.startswith("get"): continue + if "operator=" in functionSig: continue + if "&&" in functionSig: continue + tmp3list.append((sourceLoc, functionSig, callInfo[3] + " " + callInfo[2], callValue)) + # sort results by filename:lineno def natural_sort_key(s, _nsre=re.compile('([0-9]+)')): return [int(text) if text.isdigit() else text.lower() for text in re.split(_nsre, s)] tmp1list.sort(key=lambda v: natural_sort_key(v[0])) +tmp2list.sort(key=lambda v: natural_sort_key(v[0])) +tmp3list.sort(key=lambda v: natural_sort_key(v[0])) # print out the results -with open("loplugin.constantparam.report", "wt") as f: +with open("loplugin.constantparam.report-booleans", "wt") as f: for v in tmp1list: f.write(v[0] + "\n") f.write(" " + v[1] + "\n") f.write(" " + v[2] + "\n") + f.write(" " + v[3] + "\n") +with open("loplugin.constantparam.report-numbers", "wt") as f: + for v in tmp2list: + f.write(v[0] + "\n") + f.write(" " + v[1] + "\n") + f.write(" " + v[2] + "\n") + f.write(" " + v[3] + "\n") +with open("loplugin.constantparam.report-constructors", "wt") as f: + for v in tmp3list: + f.write(v[0] + "\n") + f.write(" " + v[1] + "\n") + f.write(" " + v[2] + "\n") + f.write(" " + v[3] + "\n") # ------------------------------------------------------------- # Now a fun set of heuristics to look for methods that |