clang-tools  11.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  IncludedTypes(
26  utils::options::parseStringList(Options.get("IncludedTypes", ""))) {}
27 
29  Options.store(Opts, "IncludedTypes",
31 }
32 
33 void NonConstReferences::registerMatchers(MatchFinder *Finder) {
34  Finder->addMatcher(
35  parmVarDecl(
36  unless(isInstantiated()),
37  hasType(references(
38  qualType(unless(isConstQualified())).bind("referenced_type"))),
39  unless(hasType(rValueReferenceType())))
40  .bind("param"),
41  this);
42 }
43 
44 void NonConstReferences::check(const MatchFinder::MatchResult &Result) {
45  const auto *Parameter = Result.Nodes.getNodeAs<ParmVarDecl>("param");
46  const auto *Function =
47  dyn_cast_or_null<FunctionDecl>(Parameter->getParentFunctionOrMethod());
48 
49  if (Function == nullptr || Function->isImplicit())
50  return;
51 
52  if (Function->getLocation().isMacroID())
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(IncludedTypes.begin(), IncludedTypes.end(),
71  [&](llvm::StringRef ExplicitType) {
72  return ReferencedType.getCanonicalType().getAsString(
73  Result.Context->getPrintingPolicy()) ==
74  ExplicitType;
75  }) != IncludedTypes.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
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:114
clang::tidy::google::runtime::NonConstReferences::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: NonConstReferences.cpp:44
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::google::runtime::NonConstReferences::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: NonConstReferences.cpp:33
clang::tidy::utils::options::serializeStringList
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
Definition: OptionsUtils.cpp:30
clang::tidy::utils::options::parseStringList
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
Definition: OptionsUtils.cpp:18
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:471
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:76
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:55
clang::tidy::google::runtime::NonConstReferences::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: NonConstReferences.cpp:28
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyCheck::OptionsView::store
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.
Definition: ClangTidyCheck.cpp:152
NonConstReferences.h
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:111