diff options
author | Nathan James <n.james93@hotmail.co.uk> | 2020-01-14 14:05:45 -0500 |
---|---|---|
committer | Aaron Ballman <aaron@aaronballman.com> | 2020-01-14 14:06:46 -0500 |
commit | 36fcbb838c8f293f46bfed78c6ed8c177f1e3485 (patch) | |
tree | 70a4e2a217459d962881306c52cf210fa875ce33 /clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp | |
parent | cd800f3b226b25142f233beca846715fc601809b (diff) | |
download | bcm5719-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.cpp | 294 |
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 |