|  | //===--- 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 |