clang-tools  9.0.0
NonConstReferences.cpp
Go to the documentation of this file.
1 //===--- NonConstReferences.cpp - clang-tidy --------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "NonConstReferences.h"
10 #include "../utils/OptionsUtils.h"
11 #include "clang/AST/DeclBase.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace google {
20 namespace runtime {
21 
22 NonConstReferences::NonConstReferences(StringRef Name,
23  ClangTidyContext *Context)
24  : ClangTidyCheck(Name, Context),
25  WhiteListTypes(
26  utils::options::parseStringList(Options.get("WhiteListTypes", ""))) {}
27 
29  Options.store(Opts, "WhiteListTypes",
30  utils::options::serializeStringList(WhiteListTypes));
31 }
32 
33 void NonConstReferences::registerMatchers(MatchFinder *Finder) {
34  if (!getLangOpts().CPlusPlus)
35  return;
36 
37  Finder->addMatcher(
38  parmVarDecl(
39  unless(isInstantiated()),
40  hasType(references(
41  qualType(unless(isConstQualified())).bind("referenced_type"))),
42  unless(hasType(rValueReferenceType())))
43  .bind("param"),
44  this);
45 }
46 
47 void NonConstReferences::check(const MatchFinder::MatchResult &Result) {
48  const auto *Parameter = Result.Nodes.getNodeAs<ParmVarDecl>("param");
49  const auto *Function =
50  dyn_cast_or_null<FunctionDecl>(Parameter->getParentFunctionOrMethod());
51 
52  if (Function == nullptr || Function->isImplicit())
53  return;
54 
55  if (!Function->isCanonicalDecl())
56  return;
57 
58  if (const auto *Method = dyn_cast<CXXMethodDecl>(Function)) {
59  // Don't warn on implementations of an interface using references.
60  if (Method->begin_overridden_methods() != Method->end_overridden_methods())
61  return;
62  // Don't warn on lambdas, as they frequently have to conform to the
63  // interface defined elsewhere.
64  if (Method->getParent()->isLambda())
65  return;
66  }
67 
68  auto ReferencedType = *Result.Nodes.getNodeAs<QualType>("referenced_type");
69 
70  if (std::find_if(WhiteListTypes.begin(), WhiteListTypes.end(),
71  [&](llvm::StringRef WhiteListType) {
72  return ReferencedType.getCanonicalType().getAsString(
73  Result.Context->getPrintingPolicy()) ==
74  WhiteListType;
75  }) != WhiteListTypes.end())
76  return;
77 
78  // Don't warn on function references, they shouldn't be constant.
79  if (ReferencedType->isFunctionProtoType())
80  return;
81 
82  // Don't warn on dependent types in templates.
83  if (ReferencedType->isDependentType())
84  return;
85 
86  if (Function->isOverloadedOperator()) {
87  switch (Function->getOverloadedOperator()) {
88  case clang::OO_LessLess:
89  case clang::OO_PlusPlus:
90  case clang::OO_MinusMinus:
91  case clang::OO_PlusEqual:
92  case clang::OO_MinusEqual:
93  case clang::OO_StarEqual:
94  case clang::OO_SlashEqual:
95  case clang::OO_PercentEqual:
96  case clang::OO_LessLessEqual:
97  case clang::OO_GreaterGreaterEqual:
98  case clang::OO_PipeEqual:
99  case clang::OO_CaretEqual:
100  case clang::OO_AmpEqual:
101  // Don't warn on the first parameter of operator<<(Stream&, ...),
102  // operator++, operator-- and operation+assignment operators.
103  if (Function->getParamDecl(0) == Parameter)
104  return;
105  break;
106  case clang::OO_GreaterGreater: {
107  auto isNonConstRef = [](clang::QualType T) {
108  return T->isReferenceType() &&
109  !T.getNonReferenceType().isConstQualified();
110  };
111  // Don't warn on parameters of stream extractors:
112  // Stream& operator>>(Stream&, Value&);
113  // Both parameters should be non-const references by convention.
114  if (isNonConstRef(Function->getParamDecl(0)->getType()) &&
115  (Function->getNumParams() < 2 || // E.g. member operator>>.
116  isNonConstRef(Function->getParamDecl(1)->getType())) &&
117  isNonConstRef(Function->getReturnType()))
118  return;
119  break;
120  }
121  default:
122  break;
123  }
124  }
125 
126  // Some functions use references to comply with established standards.
127  if (Function->getDeclName().isIdentifier() && Function->getName() == "swap")
128  return;
129 
130  // iostream parameters are typically passed by non-const reference.
131  if (StringRef(ReferencedType.getAsString()).endswith("stream"))
132  return;
133 
134  if (Parameter->getName().empty()) {
135  diag(Parameter->getLocation(), "non-const reference parameter at index %0, "
136  "make it const or use a pointer")
137  << Parameter->getFunctionScopeIndex();
138  } else {
139  diag(Parameter->getLocation(),
140  "non-const reference parameter %0, make it const or use a pointer")
141  << Parameter;
142  }
143 }
144 
145 } // namespace runtime
146 } // namespace google
147 } // namespace tidy
148 } // namespace clang
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.