Extend misc-unused-parameters to delete parameters of local functions.

Also see: llvm.org/PR24180.

llvm-svn: 242659
diff --git a/clang-tools-extra/clang-tidy/misc/UnusedParametersCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnusedParametersCheck.cpp
index 170a274..bfc213e 100644
--- a/clang-tools-extra/clang-tidy/misc/UnusedParametersCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/UnusedParametersCheck.cpp
@@ -22,6 +22,38 @@
       this);
 }
 
+static FixItHint removeParameter(const FunctionDecl *Function, unsigned Index) {
+  const ParmVarDecl *Param = Function->getParamDecl(Index);
+  unsigned ParamCount = Function->getNumParams();
+  SourceRange RemovalRange = Param->getSourceRange();
+  if (ParamCount == 1)
+    return FixItHint::CreateRemoval(RemovalRange);
+
+  if (Index == 0)
+    RemovalRange.setEnd(
+        Function->getParamDecl(Index + 1)->getLocStart().getLocWithOffset(-1));
+  else
+    RemovalRange.setBegin(
+        Function->getParamDecl(Index - 1)->getLocEnd().getLocWithOffset(1));
+
+  return FixItHint::CreateRemoval(RemovalRange);
+}
+
+static FixItHint removeArgument(const CallExpr *Call, unsigned Index) {
+  unsigned ArgCount = Call->getNumArgs();
+  const Expr *Arg = Call->getArg(Index);
+  SourceRange RemovalRange = Arg->getSourceRange();
+  if (ArgCount == 1)
+    return FixItHint::CreateRemoval(RemovalRange);
+  if (Index == 0)
+    RemovalRange.setEnd(
+        Call->getArg(Index + 1)->getLocStart().getLocWithOffset(-1));
+  else
+    RemovalRange.setBegin(
+        Call->getArg(Index - 1)->getLocEnd().getLocWithOffset(1));
+  return FixItHint::CreateRemoval(RemovalRange);
+}
+
 void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function");
   if (!Function->doesThisDeclarationHaveABody())
@@ -33,9 +65,39 @@
   auto MyDiag = diag(Param->getLocation(), "parameter '%0' is unused")
                 << Param->getName();
 
-  SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd());
-  MyDiag << FixItHint::CreateReplacement(
-      RemovalRange, (Twine(" /*") + Param->getName() + "*/").str());
+  auto UsedByRef = [&] {
+    return !ast_matchers::match(
+                decl(hasDescendant(
+                    declRefExpr(to(equalsNode(Function)),
+                                unless(hasAncestor(
+                                    callExpr(callee(equalsNode(Function)))))))),
+                *Result.Context->getTranslationUnitDecl(), *Result.Context)
+                .empty();
+  };
+
+  // Comment out parameter name for non-local functions.
+  if ((Function->isExternallyVisible() &&
+       Function->getStorageClass() != StorageClass::SC_Static) ||
+      UsedByRef()) {
+    SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd());
+    MyDiag << FixItHint::CreateReplacement(
+        RemovalRange, (Twine(" /*") + Param->getName() + "*/").str());
+    return;
+  }
+
+  // Handle local functions by deleting the parameters.
+  unsigned ParamIndex = Param->getFunctionScopeIndex();
+  // Fix all redeclarations.
+  for (const FunctionDecl *FD : Function->redecls())
+    MyDiag << removeParameter(FD, ParamIndex);
+
+  // Fix all call sites.
+  auto CallMatches = ast_matchers::match(
+      decl(forEachDescendant(
+          callExpr(callee(functionDecl(equalsNode(Function)))).bind("x"))),
+      *Result.Context->getTranslationUnitDecl(), *Result.Context);
+  for (const auto &Match : CallMatches)
+    MyDiag << removeArgument(Match.getNodeAs<CallExpr>("x"), ParamIndex);
 }
 
 } // namespace tidy