summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
diff options
context:
space:
mode:
authorNathan James <n.james93@hotmail.co.uk>2020-01-14 14:05:45 -0500
committerAaron Ballman <aaron@aaronballman.com>2020-01-14 14:06:46 -0500
commit36fcbb838c8f293f46bfed78c6ed8c177f1e3485 (patch)
tree70a4e2a217459d962881306c52cf210fa875ce33 /clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
parentcd800f3b226b25142f233beca846715fc601809b (diff)
downloadbcm5719-llvm-36fcbb838c8f293f46bfed78c6ed8c177f1e3485.tar.gz
bcm5719-llvm-36fcbb838c8f293f46bfed78c6ed8c177f1e3485.zip
Added readability-qualified-auto check
Adds a check that detects any auto variables that are deduced to a pointer or a const pointer then adds in the const and asterisk according. Will also check auto L value references that could be written as const. This relates to the coding standard https://llvm.org/docs/CodingStandards.html#beware-unnecessary-copies-with-auto
Diffstat (limited to 'clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp294
1 files changed, 294 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
new file mode 100644
index 00000000000..969927fedc3
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
@@ -0,0 +1,294 @@
+//===--- QualifiedAutoCheck.cpp - clang-tidy ------------------------------===//
+//
+// 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 "QualifiedAutoCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+// FIXME move to ASTMatchers
+AST_MATCHER_P(QualType, hasUnqualifiedType,
+ ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
+ return InnerMatcher.matches(Node.getUnqualifiedType(), Finder, Builder);
+}
+
+enum class Qualifier { Const, Volatile, Restrict };
+
+llvm::Optional<Token> findQualToken(const VarDecl *Decl, Qualifier Qual,
+ const MatchFinder::MatchResult &Result) {
+ // Since either of the locs can be in a macro, use `makeFileCharRange` to be
+ // sure that we have a consistent `CharSourceRange`, located entirely in the
+ // source file.
+
+ assert(Qual == Qualifier::Const || Qual == Qualifier::Volatile ||
+ Qual == Qualifier::Restrict && "Invalid Qualifier");
+
+ SourceLocation BeginLoc = Decl->getQualifierLoc().getBeginLoc();
+ if (BeginLoc.isInvalid())
+ BeginLoc = Decl->getBeginLoc();
+ SourceLocation EndLoc = Decl->getLocation();
+
+ CharSourceRange FileRange = Lexer::makeFileCharRange(
+ CharSourceRange::getCharRange(BeginLoc, EndLoc), *Result.SourceManager,
+ Result.Context->getLangOpts());
+
+ if (FileRange.isInvalid())
+ return llvm::None;
+
+ tok::TokenKind Tok =
+ Qual == Qualifier::Const
+ ? tok::kw_const
+ : Qual == Qualifier::Volatile ? tok::kw_volatile : tok::kw_restrict;
+
+ return utils::lexer::getQualifyingToken(Tok, FileRange, *Result.Context,
+ *Result.SourceManager);
+}
+
+llvm::Optional<SourceRange>
+getTypeSpecifierLocation(const VarDecl *Var,
+ const MatchFinder::MatchResult &Result) {
+ SourceRange TypeSpecifier(
+ Var->getTypeSpecStartLoc(),
+ Var->getTypeSpecEndLoc().getLocWithOffset(Lexer::MeasureTokenLength(
+ Var->getTypeSpecEndLoc(), *Result.SourceManager,
+ Result.Context->getLangOpts())));
+
+ if (TypeSpecifier.getBegin().isMacroID() ||
+ TypeSpecifier.getEnd().isMacroID())
+ return llvm::None;
+ return TypeSpecifier;
+}
+
+llvm::Optional<SourceRange> mergeReplacementRange(SourceRange &TypeSpecifier,
+ const Token &ConstToken) {
+ if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) {
+ TypeSpecifier.setBegin(ConstToken.getLocation());
+ return llvm::None;
+ }
+ if (TypeSpecifier.getEnd().getLocWithOffset(1) == ConstToken.getLocation()) {
+ TypeSpecifier.setEnd(ConstToken.getEndLoc());
+ return llvm::None;
+ }
+ return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc());
+}
+
+bool isPointerConst(QualType QType) {
+ QualType Pointee = QType->getPointeeType();
+ assert(!Pointee.isNull() && "can't have a null Pointee");
+ return Pointee.isConstQualified();
+}
+
+bool isAutoPointerConst(QualType QType) {
+ QualType Pointee =
+ cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar();
+ assert(!Pointee.isNull() && "can't have a null Pointee");
+ return Pointee.isConstQualified();
+}
+
+} // namespace
+
+void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return; // Auto deduction not used in 'C or C++03 and earlier', so don't
+ // register Matchers.
+ auto ExplicitSingleVarDecl =
+ [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
+ llvm::StringRef ID) {
+ return declStmt(
+ unless(isInTemplateInstantiation()),
+ hasSingleDecl(
+ varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
+ };
+ auto ExplicitSingleVarDeclInTemplate =
+ [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,
+ llvm::StringRef ID) {
+ return declStmt(
+ isInTemplateInstantiation(),
+ hasSingleDecl(
+ varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));
+ };
+
+ auto IsBoundToType = refersToType(equalsBoundNode("type"));
+
+ Finder->addMatcher(
+ ExplicitSingleVarDecl(hasType(autoType(hasDeducedType(
+ pointerType(pointee(unless(functionType())))))),
+ "auto"),
+ this);
+
+ Finder->addMatcher(
+ ExplicitSingleVarDeclInTemplate(
+ allOf(hasType(autoType(hasDeducedType(pointerType(
+ pointee(hasUnqualifiedType(qualType().bind("type")),
+ unless(functionType())))))),
+ anyOf(hasAncestor(
+ functionDecl(hasAnyTemplateArgument(IsBoundToType))),
+ hasAncestor(classTemplateSpecializationDecl(
+ hasAnyTemplateArgument(IsBoundToType))))),
+ "auto"),
+ this);
+ Finder->addMatcher(ExplicitSingleVarDecl(
+ hasType(pointerType(pointee(autoType()))), "auto_ptr"),
+ this);
+ Finder->addMatcher(
+ ExplicitSingleVarDecl(hasType(lValueReferenceType(pointee(autoType()))),
+ "auto_ref"),
+ this);
+}
+
+void QualifiedAutoCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto")) {
+ SourceRange TypeSpecifier;
+ if (llvm::Optional<SourceRange> TypeSpec =
+ getTypeSpecifierLocation(Var, Result)) {
+ TypeSpecifier = *TypeSpec;
+ } else
+ return;
+
+ llvm::SmallVector<SourceRange, 4> RemoveQualifiersRange;
+ auto CheckQualifier = [&](bool IsPresent, Qualifier Qual) {
+ if (IsPresent) {
+ llvm::Optional<Token> Token = findQualToken(Var, Qual, Result);
+ if (!Token || Token->getLocation().isMacroID())
+ return true; // Disregard this VarDecl.
+ if (llvm::Optional<SourceRange> Result =
+ mergeReplacementRange(TypeSpecifier, *Token))
+ RemoveQualifiersRange.push_back(*Result);
+ }
+ return false;
+ };
+
+ bool IsLocalConst = Var->getType().isLocalConstQualified();
+ bool IsLocalVolatile = Var->getType().isLocalVolatileQualified();
+ bool IsLocalRestrict = Var->getType().isLocalRestrictQualified();
+
+ if (CheckQualifier(IsLocalConst, Qualifier::Const))
+ return;
+ if (CheckQualifier(IsLocalVolatile, Qualifier::Volatile))
+ return;
+ if (CheckQualifier(IsLocalRestrict, Qualifier::Restrict))
+ return;
+
+ // Check for bridging the gap between the asterisk and name.
+ if (Var->getLocation() == TypeSpecifier.getEnd().getLocWithOffset(1))
+ TypeSpecifier.setEnd(TypeSpecifier.getEnd().getLocWithOffset(1));
+
+ CharSourceRange FixItRange = CharSourceRange::getCharRange(TypeSpecifier);
+ if (FixItRange.isInvalid())
+ return;
+
+ SourceLocation FixitLoc = FixItRange.getBegin();
+ for (SourceRange &Range : RemoveQualifiersRange) {
+ if (Range.getBegin() < FixitLoc)
+ FixitLoc = Range.getBegin();
+ }
+
+ std::string ReplStr = [&] {
+ llvm::StringRef PtrConst = isPointerConst(Var->getType()) ? "const " : "";
+ llvm::StringRef LocalConst = IsLocalConst ? "const " : "";
+ llvm::StringRef LocalVol = IsLocalVolatile ? "volatile " : "";
+ llvm::StringRef LocalRestrict = IsLocalRestrict ? "__restrict " : "";
+ return (PtrConst + "auto *" + LocalConst + LocalVol + LocalRestrict)
+ .str();
+ }();
+
+ DiagnosticBuilder Diag =
+ diag(FixitLoc, "'%0%1%2auto %3' can be declared as '%4%3'")
+ << (IsLocalConst ? "const " : "")
+ << (IsLocalVolatile ? "volatile " : "")
+ << (IsLocalRestrict ? "__restrict " : "") << Var->getName() << ReplStr;
+
+ for (SourceRange &Range : RemoveQualifiersRange) {
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()));
+ }
+
+ Diag << FixItHint::CreateReplacement(FixItRange, ReplStr);
+ return;
+ }
+ if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ptr")) {
+ if (!isPointerConst(Var->getType()))
+ return; // Pointer isn't const, no need to add const qualifier.
+ if (!isAutoPointerConst(Var->getType()))
+ return; // Const isnt wrapped in the auto type, so must be declared
+ // explicitly.
+
+ if (Var->getType().isLocalConstQualified()) {
+ llvm::Optional<Token> Token =
+ findQualToken(Var, Qualifier::Const, Result);
+ if (!Token || Token->getLocation().isMacroID())
+ return;
+ }
+ if (Var->getType().isLocalVolatileQualified()) {
+ llvm::Optional<Token> Token =
+ findQualToken(Var, Qualifier::Volatile, Result);
+ if (!Token || Token->getLocation().isMacroID())
+ return;
+ }
+ if (Var->getType().isLocalRestrictQualified()) {
+ llvm::Optional<Token> Token =
+ findQualToken(Var, Qualifier::Restrict, Result);
+ if (!Token || Token->getLocation().isMacroID())
+ return;
+ }
+
+ CharSourceRange FixItRange;
+ if (llvm::Optional<SourceRange> TypeSpec =
+ getTypeSpecifierLocation(Var, Result)) {
+ FixItRange = CharSourceRange::getCharRange(*TypeSpec);
+ if (FixItRange.isInvalid())
+ return;
+ } else
+ return;
+
+ DiagnosticBuilder Diag =
+ diag(FixItRange.getBegin(),
+ "'auto *%0%1%2' can be declared as 'const auto *%0%1%2'")
+ << (Var->getType().isLocalConstQualified() ? "const " : "")
+ << (Var->getType().isLocalVolatileQualified() ? "volatile " : "")
+ << Var->getName();
+ Diag << FixItHint::CreateReplacement(FixItRange, "const auto *");
+ return;
+ }
+ if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ref")) {
+ if (!isPointerConst(Var->getType()))
+ return; // Pointer isn't const, no need to add const qualifier.
+ if (!isAutoPointerConst(Var->getType()))
+ // Const isnt wrapped in the auto type, so must be declared explicitly.
+ return;
+
+ CharSourceRange FixItRange;
+ if (llvm::Optional<SourceRange> TypeSpec =
+ getTypeSpecifierLocation(Var, Result)) {
+ FixItRange = CharSourceRange::getCharRange(*TypeSpec);
+ if (FixItRange.isInvalid())
+ return;
+ } else
+ return;
+
+ DiagnosticBuilder Diag =
+ diag(FixItRange.getBegin(),
+ "'auto &%0' can be declared as 'const auto &%0'")
+ << Var->getName();
+ Diag << FixItHint::CreateReplacement(FixItRange, "const auto &");
+ return;
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
OpenPOWER on IntegriCloud