blob: 856bc5bc25dcc1446122277f150fb373cb97edea [file] [log] [blame]
//===--- NonConstReferences.cpp - clang-tidy --------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "NonConstReferences.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/DeclBase.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace google {
namespace runtime {
NonConstReferences::NonConstReferences(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
WhiteListTypes(
utils::options::parseStringList(Options.get("WhiteListTypes", ""))) {}
void NonConstReferences::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "WhiteListTypes",
utils::options::serializeStringList(WhiteListTypes));
}
void NonConstReferences::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
Finder->addMatcher(
parmVarDecl(
unless(isInstantiated()),
hasType(references(
qualType(unless(isConstQualified())).bind("referenced_type"))),
unless(hasType(rValueReferenceType())))
.bind("param"),
this);
}
void NonConstReferences::check(const MatchFinder::MatchResult &Result) {
const auto *Parameter = Result.Nodes.getNodeAs<ParmVarDecl>("param");
const auto *Function =
dyn_cast_or_null<FunctionDecl>(Parameter->getParentFunctionOrMethod());
if (Function == nullptr || Function->isImplicit())
return;
if (!Function->isCanonicalDecl())
return;
if (const auto *Method = dyn_cast<CXXMethodDecl>(Function)) {
// Don't warn on implementations of an interface using references.
if (Method->begin_overridden_methods() != Method->end_overridden_methods())
return;
// Don't warn on lambdas, as they frequently have to conform to the
// interface defined elsewhere.
if (Method->getParent()->isLambda())
return;
}
auto ReferencedType = *Result.Nodes.getNodeAs<QualType>("referenced_type");
if (std::find_if(WhiteListTypes.begin(), WhiteListTypes.end(),
[&](llvm::StringRef WhiteListType) {
return ReferencedType.getCanonicalType().getAsString(
Result.Context->getPrintingPolicy()) ==
WhiteListType;
}) != WhiteListTypes.end())
return;
// Don't warn on function references, they shouldn't be constant.
if (ReferencedType->isFunctionProtoType())
return;
// Don't warn on dependent types in templates.
if (ReferencedType->isDependentType())
return;
if (Function->isOverloadedOperator()) {
switch (Function->getOverloadedOperator()) {
case clang::OO_LessLess:
case clang::OO_PlusPlus:
case clang::OO_MinusMinus:
case clang::OO_PlusEqual:
case clang::OO_MinusEqual:
case clang::OO_StarEqual:
case clang::OO_SlashEqual:
case clang::OO_PercentEqual:
case clang::OO_LessLessEqual:
case clang::OO_GreaterGreaterEqual:
case clang::OO_PipeEqual:
case clang::OO_CaretEqual:
case clang::OO_AmpEqual:
// Don't warn on the first parameter of operator<<(Stream&, ...),
// operator++, operator-- and operation+assignment operators.
if (Function->getParamDecl(0) == Parameter)
return;
break;
case clang::OO_GreaterGreater: {
auto isNonConstRef = [](clang::QualType T) {
return T->isReferenceType() &&
!T.getNonReferenceType().isConstQualified();
};
// Don't warn on parameters of stream extractors:
// Stream& operator>>(Stream&, Value&);
// Both parameters should be non-const references by convention.
if (isNonConstRef(Function->getParamDecl(0)->getType()) &&
(Function->getNumParams() < 2 || // E.g. member operator>>.
isNonConstRef(Function->getParamDecl(1)->getType())) &&
isNonConstRef(Function->getReturnType()))
return;
break;
}
default:
break;
}
}
// Some functions use references to comply with established standards.
if (Function->getDeclName().isIdentifier() && Function->getName() == "swap")
return;
// iostream parameters are typically passed by non-const reference.
if (StringRef(ReferencedType.getAsString()).endswith("stream"))
return;
if (Parameter->getName().empty()) {
diag(Parameter->getLocation(), "non-const reference parameter at index %0, "
"make it const or use a pointer")
<< Parameter->getFunctionScopeIndex();
} else {
diag(Parameter->getLocation(),
"non-const reference parameter %0, make it const or use a pointer")
<< Parameter;
}
}
} // namespace runtime
} // namespace google
} // namespace tidy
} // namespace clang