blob: d1bc1534c70d295651cec948a59cebf6838d769b [file] [log] [blame]
Alexander Kornienko26e46d82016-02-02 17:27:01 +00001//===--- NonConstReferences.cpp - clang-tidy --------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "NonConstReferences.h"
Haojian Wueaf77912016-10-10 16:38:11 +000011#include "../utils/OptionsUtils.h"
Alexander Kornienko26e46d82016-02-02 17:27:01 +000012#include "clang/AST/DeclBase.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/ASTMatchers/ASTMatchers.h"
15
16using namespace clang::ast_matchers;
17
18namespace clang {
19namespace tidy {
20namespace google {
21namespace runtime {
22
Haojian Wueaf77912016-10-10 16:38:11 +000023NonConstReferences::NonConstReferences(StringRef Name,
24 ClangTidyContext *Context)
25 : ClangTidyCheck(Name, Context),
26 WhiteListTypes(
27 utils::options::parseStringList(Options.get("WhiteListTypes", ""))) {}
28
29void NonConstReferences::storeOptions(ClangTidyOptions::OptionMap &Opts) {
30 Options.store(Opts, "WhiteListTypes",
31 utils::options::serializeStringList(WhiteListTypes));
32}
33
Alexander Kornienko26e46d82016-02-02 17:27:01 +000034void NonConstReferences::registerMatchers(MatchFinder *Finder) {
Haojian Wueaf77912016-10-10 16:38:11 +000035 if (!getLangOpts().CPlusPlus)
36 return;
37
Alexander Kornienko26e46d82016-02-02 17:27:01 +000038 Finder->addMatcher(
39 parmVarDecl(
40 unless(isInstantiated()),
41 hasType(references(
42 qualType(unless(isConstQualified())).bind("referenced_type"))),
43 unless(hasType(rValueReferenceType())))
44 .bind("param"),
45 this);
46}
47
48void NonConstReferences::check(const MatchFinder::MatchResult &Result) {
49 const auto *Parameter = Result.Nodes.getNodeAs<ParmVarDecl>("param");
50 const auto *Function =
51 dyn_cast_or_null<FunctionDecl>(Parameter->getParentFunctionOrMethod());
52
53 if (Function == nullptr || Function->isImplicit())
54 return;
55
56 if (!Function->isCanonicalDecl())
57 return;
58
59 if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Function)) {
60 // Don't warn on implementations of an interface using references.
61 if (Method->begin_overridden_methods() != Method->end_overridden_methods())
62 return;
63 // Don't warn on lambdas, as they frequently have to conform to the
64 // interface defined elsewhere.
65 if (Method->getParent()->isLambda())
66 return;
67 }
68
69 auto ReferencedType = *Result.Nodes.getNodeAs<QualType>("referenced_type");
Haojian Wueaf77912016-10-10 16:38:11 +000070
71 if (std::find_if(WhiteListTypes.begin(), WhiteListTypes.end(),
72 [&](llvm::StringRef WhiteListType) {
73 return ReferencedType.getCanonicalType().getAsString(
74 Result.Context->getPrintingPolicy()) ==
75 WhiteListType;
76 }) != WhiteListTypes.end())
77 return;
78
Alexander Kornienko26e46d82016-02-02 17:27:01 +000079 // Don't warn on function references, they shouldn't be constant.
80 if (ReferencedType->isFunctionProtoType())
81 return;
82
83 // Don't warn on dependent types in templates.
84 if (ReferencedType->isDependentType())
85 return;
86
87 if (Function->isOverloadedOperator()) {
88 switch (Function->getOverloadedOperator()) {
89 case clang::OO_LessLess:
90 case clang::OO_PlusPlus:
91 case clang::OO_MinusMinus:
92 case clang::OO_PlusEqual:
93 case clang::OO_MinusEqual:
94 case clang::OO_StarEqual:
95 case clang::OO_SlashEqual:
96 case clang::OO_PercentEqual:
97 case clang::OO_LessLessEqual:
98 case clang::OO_GreaterGreaterEqual:
99 case clang::OO_PipeEqual:
100 case clang::OO_CaretEqual:
101 case clang::OO_AmpEqual:
102 // Don't warn on the first parameter of operator<<(Stream&, ...),
103 // operator++, operator-- and operation+assignment operators.
104 if (Function->getParamDecl(0) == Parameter)
105 return;
106 break;
107 case clang::OO_GreaterGreater: {
108 auto isNonConstRef = [](clang::QualType T) {
109 return T->isReferenceType() &&
110 !T.getNonReferenceType().isConstQualified();
111 };
112 // Don't warn on parameters of stream extractors:
113 // Stream& operator>>(Stream&, Value&);
114 // Both parameters should be non-const references by convention.
115 if (isNonConstRef(Function->getParamDecl(0)->getType()) &&
116 (Function->getNumParams() < 2 || // E.g. member operator>>.
117 isNonConstRef(Function->getParamDecl(1)->getType())) &&
118 isNonConstRef(Function->getReturnType()))
119 return;
120 break;
121 }
122 default:
123 break;
124 }
125 }
126
127 // Some functions use references to comply with established standards.
128 if (Function->getDeclName().isIdentifier() && Function->getName() == "swap")
129 return;
130
131 // iostream parameters are typically passed by non-const reference.
132 if (StringRef(ReferencedType.getAsString()).endswith("stream"))
133 return;
134
Haojian Wu4d67ec32016-02-04 14:06:49 +0000135 if (Parameter->getName().empty()) {
136 diag(Parameter->getLocation(),
137 "non-const reference parameter at index %0, "
138 "make it const or use a pointer")
139 << Parameter->getFunctionScopeIndex();
140 } else {
141 diag(Parameter->getLocation(),
Benjamin Kramera62e2232016-04-07 14:55:25 +0000142 "non-const reference parameter %0, make it const or use a pointer")
143 << Parameter;
Haojian Wu4d67ec32016-02-04 14:06:49 +0000144 }
Alexander Kornienko26e46d82016-02-02 17:27:01 +0000145}
146
147} // namespace runtime
148} // namespace google
149} // namespace tidy
150} // namespace clang