aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-tidy/readability/AvoidConstParamsInDeclsCheck.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-tidy/readability/AvoidConstParamsInDeclsCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/readability/AvoidConstParamsInDeclsCheck.cpp99
1 files changed, 99 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidConstParamsInDeclsCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidConstParamsInDeclsCheck.cpp
new file mode 100644
index 0000000..0e1c389
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/AvoidConstParamsInDeclsCheck.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AvoidConstParamsInDeclsCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+static SourceRange getTypeRange(const ParmVarDecl &Param) {
+ return {Param.getBeginLoc(), Param.getLocation().getLocWithOffset(-1)};
+}
+
+// Finds the location of the qualifying `const` token in the `ParmValDecl`'s
+// return type. Returns `std::nullopt` when the parm type is not
+// `const`-qualified like when the type is an alias or a macro.
+static std::optional<Token>
+findConstToRemove(const ParmVarDecl &Param,
+ const MatchFinder::MatchResult &Result) {
+
+ CharSourceRange FileRange = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(getTypeRange(Param)),
+ *Result.SourceManager, Result.Context->getLangOpts());
+
+ if (FileRange.isInvalid())
+ return std::nullopt;
+
+ return tidy::utils::lexer::getQualifyingToken(
+ tok::kw_const, FileRange, *Result.Context, *Result.SourceManager);
+}
+
+void AvoidConstParamsInDeclsCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+}
+
+void AvoidConstParamsInDeclsCheck::registerMatchers(MatchFinder *Finder) {
+ const auto ConstParamDecl =
+ parmVarDecl(hasType(qualType(isConstQualified()))).bind("param");
+ Finder->addMatcher(functionDecl(unless(isDefinition()),
+ has(typeLoc(forEach(ConstParamDecl))))
+ .bind("func"),
+ this);
+}
+
+void AvoidConstParamsInDeclsCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+ const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+
+ if (!Param->getType().isLocalConstQualified())
+ return;
+
+ if (IgnoreMacros &&
+ (Param->getBeginLoc().isMacroID() || Param->getEndLoc().isMacroID())) {
+ // Suppress the check if macros are involved.
+ return;
+ }
+
+ const auto Tok = findConstToRemove(*Param, Result);
+ const auto ConstLocation = Tok ? Tok->getLocation() : Param->getBeginLoc();
+
+ auto Diag = diag(ConstLocation,
+ "parameter %0 is const-qualified in the function "
+ "declaration; const-qualification of parameters only has an "
+ "effect in function definitions");
+ if (Param->getName().empty()) {
+ for (unsigned int I = 0; I < Func->getNumParams(); ++I) {
+ if (Param == Func->getParamDecl(I)) {
+ Diag << (I + 1);
+ break;
+ }
+ }
+ } else {
+ Diag << Param;
+ }
+
+ if (Param->getBeginLoc().isMacroID() != Param->getEndLoc().isMacroID()) {
+ // Do not offer a suggestion if the part of the variable declaration comes
+ // from a macro.
+ return;
+ }
+ if (!Tok)
+ return;
+
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(Tok->getLocation(), Tok->getLocation()));
+}
+
+} // namespace clang::tidy::readability