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