angelgarcia created this revision. angelgarcia added a reviewer: alexfh. angelgarcia added subscribers: cfe-commits, klimek. angelgarcia changed the visibility of this Differential Revision from "Public (No Login Required)" to "All Users".
Migrate UseAuto from clang-modernize to clang-tidy. http://reviews.llvm.org/D12231 Files: clang-tidy/modernize/CMakeLists.txt clang-tidy/modernize/ModernizeTidyModule.cpp clang-tidy/modernize/UseAutoCheck.cpp clang-tidy/modernize/UseAutoCheck.h test/clang-tidy/Inputs/modernize-use-auto/containers.h test/clang-tidy/modernize-use-auto-iterator.cpp test/clang-tidy/modernize-use-auto-new.cpp
Index: test/clang-tidy/modernize-use-auto-new.cpp =================================================================== --- test/clang-tidy/modernize-use-auto-new.cpp +++ test/clang-tidy/modernize-use-auto-new.cpp @@ -0,0 +1,96 @@ +// RUN: %python %S/check_clang_tidy.py %s modernize-use-auto %t + +class MyType {}; + +class MyDerivedType : public MyType {}; + +// FIXME: the replacement sometimes results in two consecutive spaces after +// the word 'auto' (due to the presence of spaces at both sides of '*'). +void auto_new() { + MyType *a_new = new MyType(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto a_new = new MyType(); + + static MyType *a_static = new MyType(); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto [modernize-use-auto] + // CHECK-FIXES: static auto a_static = new MyType(); + + MyType *derived = new MyDerivedType(); + + void *vd = new MyType(); + + // CV-qualifier tests. + // + // NOTE : the form "type const" is expected here because of a deficiency in + // TypeLoc where CV qualifiers are not considered part of the type location + // info. That is, all that is being replaced in each case is "MyType *" and + // not "MyType * const". + static MyType * const d_static = new MyType(); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto [modernize-use-auto] + // CHECK-FIXES: static auto const d_static = new MyType(); + + MyType * const a_const = new MyType(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto const a_const = new MyType(); + + MyType * volatile vol = new MyType(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto volatile vol = new MyType(); + + int (**func)(int, int) = new (int(*[5])(int,int)); + + int *array = new int[5]; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto array = new int[5]; + + MyType *ptr(new MyType); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto ptr(new MyType); + + MyType *ptr2{new MyType}; + + { + // Test for declaration lists. + MyType *a = new MyType(), *b = new MyType(), *c = new MyType(); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto a = new MyType(), b = new MyType(), c = new MyType(); + + // Non-initialized declaration should not be transformed. + MyType *d = new MyType(), *e; + + MyType **f = new MyType*(), **g = new MyType*(); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto f = new MyType*(), g = new MyType*(); + + // Mismatching types in declaration lists should not be transformed. + MyType *h = new MyType(), **i = new MyType*(); + + // '*' shouldn't be removed in case of mismatching types with multiple + // declarations. + MyType *j = new MyType(), *k = new MyType(), **l = new MyType*(); + } + + { + // Test for typedefs. + typedef int * int_p; + + int_p a = new int; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto a = new int; + int_p *b = new int*; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto b = new int*; + + // Test for typedefs in declarations lists. + int_p c = new int, d = new int; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto c = new int, d = new int; + + // Different types should not be transformed. + int_p e = new int, *f = new int_p; + + int_p *g = new int*, *h = new int_p; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto g = new int*, h = new int_p; + } +} Index: test/clang-tidy/modernize-use-auto-iterator.cpp =================================================================== --- test/clang-tidy/modernize-use-auto-iterator.cpp +++ test/clang-tidy/modernize-use-auto-iterator.cpp @@ -0,0 +1,320 @@ +// RUN: %python %S/check_clang_tidy.py %s modernize-use-auto %t -- \ +// RUN: -std=c++11 -I %S/Inputs/modernize-use-auto + +#include "containers.h" + +void f_array() { + std::array<int, 4> C; + std::array<int, 4>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + std::array<int, 5>::reverse_iterator I2 = C.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = C.rbegin(); + + const std::array<int, 3> D; + std::array<int, 3>::const_iterator I3 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I3 = D.begin(); + + std::array<int, 5>::const_reverse_iterator I4 = D.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I4 = D.rbegin(); +} + +void f_deque() { + std::deque<int> C; + std::deque<int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + std::deque<int>::reverse_iterator I2 = C.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = C.rbegin(); + + const std::deque<int> D; + std::deque<int>::const_iterator I3 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I3 = D.begin(); + + std::deque<int>::const_reverse_iterator I4 = D.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I4 = D.rbegin(); +} + +void f_forward_list() { + std::forward_list<int> C; + std::forward_list<int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + const std::forward_list<int> D; + std::forward_list<int>::const_iterator I2 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = D.begin(); +} + +void f_list() { + std::list<int> C; + std::list<int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + std::list<int>::reverse_iterator I2 = C.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = C.rbegin(); + + const std::list<int> D; + std::list<int>::const_iterator I3 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I3 = D.begin(); + std::list<int>::const_reverse_iterator I4 = D.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I4 = D.rbegin(); +} + +void f_vector() { + std::vector<int> C; + std::vector<int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + std::vector<int>::reverse_iterator I2 = C.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = C.rbegin(); + + const std::vector<int> D; + std::vector<int>::const_iterator I3 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I3 = D.begin(); + + std::vector<int>::const_reverse_iterator I4 = D.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I4 = D.rbegin(); +} + +void f_map() { + std::map<int, int> C; + std::map<int, int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + std::map<int, int>::reverse_iterator I2 = C.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = C.rbegin(); + + const std::map<int, int> D; + std::map<int, int>::const_iterator I3 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I3 = D.begin(); + + std::map<int, int>::const_reverse_iterator I4 = D.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I4 = D.rbegin(); +} + +void f_multimap() { + std::multimap<int, int> C; + std::multimap<int, int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + std::multimap<int, int>::reverse_iterator I2 = C.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = C.rbegin(); + + const std::multimap<int, int> D; + std::multimap<int, int>::const_iterator I3 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I3 = D.begin(); + + std::multimap<int, int>::const_reverse_iterator I4 = D.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I4 = D.rbegin(); +} + +void f_set() { + std::set<int> C; + std::set<int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + std::set<int>::reverse_iterator I2 = C.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = C.rbegin(); + + const std::set<int> D; + std::set<int>::const_iterator I3 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I3 = D.begin(); + + std::set<int>::const_reverse_iterator I4 = D.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I4 = D.rbegin(); +} + +void f_multiset() { + std::multiset<int> C; + std::multiset<int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + std::multiset<int>::reverse_iterator I2 = C.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = C.rbegin(); + + const std::multiset<int> D; + std::multiset<int>::const_iterator I3 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I3 = D.begin(); + + std::multiset<int>::const_reverse_iterator I4 = D.rbegin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I4 = D.rbegin(); +} + +void f_unordered_map() { + std::unordered_map<int, int> C; + std::unordered_map<int, int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + const std::unordered_map<int, int> D; + std::unordered_map<int, int>::const_iterator I2 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = D.begin(); +} + +void f_unordered_multimap() { + std::unordered_multimap<int, int> C; + std::unordered_multimap<int, int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + const std::unordered_multimap<int, int> D; + std::unordered_multimap<int, int>::const_iterator I2 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = D.begin(); +} + +void f_unordered_set() { + std::unordered_set<int> C; + std::unordered_set<int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + const std::unordered_set<int> D; + std::unordered_set<int>::const_iterator I2 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = D.begin(); +} + +void f_unordered_multiset() { + std::unordered_multiset<int> C; + std::unordered_multiset<int>::iterator I1 = C.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto [modernize-use-auto] + // CHECK-FIXES: auto I1 = C.begin(); + + const std::unordered_multiset<int> D; + std::unordered_multiset<int>::const_iterator I2 = D.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I2 = D.begin(); +} + +typedef std::vector<int>::iterator int_iterator; + +std::vector<int> Vec; +std::unordered_map<int, int> Map; + +void sugar() { + // Types with more sugar should work. Types with less should not. + int_iterator more_sugar = Vec.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto more_sugar = Vec.begin(); +} + +void initializer_list() { + // Initialization from initializer lists isn't allowed. Using 'auto' would + // result in std::initializer_list being deduced for the type. + std::unordered_map<int, int>::iterator I{Map.begin()}; + std::unordered_map<int, int>::iterator I2 = {Map.begin()}; +} + +void construction() { + // Various forms of construction. Default constructors and constructors with + // all-default parameters shouldn't get transformed. Construction from other + // types is also not allowed. + + std::unordered_map<int, int>::iterator copy(Map.begin()); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto copy(Map.begin()); + + std::unordered_map<int, int>::iterator def; + std::unordered_map<int, int>::const_iterator constI; + + // Implicit conversion. + std::unordered_map<int, int>::const_iterator constI2 = def; + std::unordered_map<int, int>::const_iterator constI3(def); + + // Explicit conversion + std::unordered_map<int, int>::const_iterator constI4 + = std::unordered_map<int, int>::const_iterator(def); + // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use auto + // CHECK-FIXES: auto constI4 + // CHECK-FIXES-NEXT: = std::unordered_map<int, int>::const_iterator(def); +} + +void pointer_to_iterator() { + int_iterator I = Vec.begin(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I = Vec.begin(); + + // Pointers and references to iterators are not transformed. + int_iterator *IPtr = &I; + int_iterator &IRef = I; +} + +void loop() { + for (std::vector<int>::iterator I = Vec.begin(); I != Vec.end(); ++I) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use auto + // CHECK-FIXES: for (auto I = Vec.begin(); I != Vec.end(); ++I) + } + + for (int_iterator I = Vec.begin(), E = Vec.end(); I != E; ++I) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use auto + // CHECK-FIXES: for (auto I = Vec.begin(), E = Vec.end(); I != E; ++I) + } + + std::vector<std::vector<int>::iterator> IterVec; + for (std::vector<int>::iterator I : IterVec) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use auto + // CHECK-FIXES: for (auto I : IterVec) + } +} + +void cv_qualifiers() { + // Make sure references and cv qualifiers don't get removed (i.e. replaced + // with just 'auto'). + const auto & I = Vec.begin(); + auto && I2 = Vec.begin(); +} + +void cleanup() { + // Passing a string as an argument to introduce a temporary object that will + // create an expression with cleanups. + std::map<std::string, int> MapFind; + std::map<std::string, int>::iterator I = MapFind.find("foo"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I = MapFind.find("foo"); +} + +void declaration_lists() { + // Declaration lists that match the declaration type with written no-list + // initializer are transformed. + std::vector<int>::iterator I = Vec.begin(), E = Vec.end(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto + // CHECK-FIXES: auto I = Vec.begin(), E = Vec.end(); + + // Declaration lists with non-initialized variables should not be transformed. + std::vector<int>::iterator J = Vec.begin(), K; +} Index: test/clang-tidy/Inputs/modernize-use-auto/containers.h =================================================================== --- test/clang-tidy/Inputs/modernize-use-auto/containers.h +++ test/clang-tidy/Inputs/modernize-use-auto/containers.h @@ -0,0 +1,253 @@ +#ifndef CONTAINERS_H +#define CONTAINERS_H + +namespace std { + +template <typename T> +class iterator { +public: + iterator() {} + iterator(const iterator<T> &iter) : ptr(iter.ptr) {} + + typedef T value_type; + typedef T *pointer; + typedef T &reference; + + reference operator*() const { return *ptr; } + pointer operator->() const { return ptr; } + iterator &operator++() { + ++ptr; + return *this; + } + iterator &operator--() { + --ptr; + return *this; + } + iterator operator++(int) { + iterator res(*this); + ++ptr; + return res; + } + iterator operator--(int) { + iterator res(*this); + --ptr; + return res; + } + bool operator!=(const iterator<T> &iter) const { + return ptr != iter.operator->(); + } + +private: + T *ptr; +}; + +template <class Iterator> +class const_iterator { +public: + const_iterator() {} + const_iterator(const Iterator &iter) : iter(iter) {} + const_iterator(const const_iterator<Iterator> &citer) : iter(citer.iter) {} + + typedef const typename Iterator::value_type value_type; + typedef const typename Iterator::pointer pointer; + typedef const typename Iterator::reference reference; + + reference operator*() const { return *iter; } + pointer operator->() const { return iter.operator->(); } + + const_iterator &operator++() { return ++iter; } + const_iterator &operator--() { return --iter; } + const_iterator operator++(int) { return iter--; } + const_iterator operator--(int) { return iter--; } + + bool operator!=(const Iterator &it) const { + return iter->operator->() != it.operator->(); + } + bool operator!=(const const_iterator<Iterator> &it) const { + return iter.operator->() != it.operator->(); + } + +private: + Iterator iter; +}; + +template <class Iterator> +class forward_iterable { +public: + forward_iterable() {} + typedef Iterator iterator; + typedef const_iterator<Iterator> const_iterator; + + iterator begin() { return _begin; } + iterator end() { return _end; } + + const_iterator begin() const { return _begin; } + const_iterator end() const { return _end; } + + const_iterator cbegin() const { return _begin; } + const_iterator cend() const { return _end; } + +private: + iterator _begin, _end; +}; + +template <class Iterator> +class reverse_iterator { +public: + reverse_iterator() {} + reverse_iterator(const Iterator &iter) : iter(iter) {} + reverse_iterator(const reverse_iterator<Iterator> &rit) : iter(rit.iter) {} + + typedef typename Iterator::value_type value_type; + typedef typename Iterator::pointer pointer; + typedef typename Iterator::reference reference; + + reference operator*() { return *iter; } + pointer operator->() { return iter.operator->(); } + + reverse_iterator &operator++() { return --iter; } + reverse_iterator &operator--() { return ++iter; } + reverse_iterator operator++(int) { return iter--; } + reverse_iterator operator--(int) { return iter++; } + +private: + Iterator iter; +}; + +template <class Iterator> +class backward_iterable { +public: + backward_iterable() {} + + typedef reverse_iterator<Iterator> reverse_iterator; + typedef const_iterator<reverse_iterator> const_reverse_iterator; + + reverse_iterator rbegin() { return _rbegin; } + reverse_iterator rend() { return _rend; } + + const_reverse_iterator rbegin() const { return _rbegin; } + const_reverse_iterator rend() const { return _rend; } + + const_reverse_iterator crbegin() const { return _rbegin; } + const_reverse_iterator crend() const { return _rend; } + +private: + reverse_iterator _rbegin, _rend; +}; + +template <class Iterator> +class bidirectional_iterable : public forward_iterable<Iterator>, + public backward_iterable<Iterator> {}; + +template <typename A, typename B> +struct pair { + pair(A f, B s) : first(f), second(s) {} + A first; + B second; +}; + +class string { +public: + string() {} + string(const char *) {} +}; + +template <typename T, int n> +class array : public backward_iterable<iterator<T>> { +public: + array() {} + + typedef T *iterator; + typedef const T *const_iterator; + + iterator begin() { return &v[0]; } + iterator end() { return &v[n - 1]; } + + const_iterator begin() const { return &v[0]; } + const_iterator end() const { return &v[n - 1]; } + + const_iterator cbegin() const { return &v[0]; } + const_iterator cend() const { return &v[n - 1]; } + +private: + T v[n]; +}; + +template <typename T> +class deque : public bidirectional_iterable<iterator<T>> { +public: + deque() {} +}; + +template <typename T> +class list : public bidirectional_iterable<iterator<T>> { +public: + list() {} +}; + +template <typename T> +class forward_list : public forward_iterable<iterator<T>> { +public: + forward_list() {} +}; + +template <typename T> +class vector : public bidirectional_iterable<iterator<T>> { +public: + vector() {} +}; + +template <typename T> +class set : public bidirectional_iterable<iterator<T>> { +public: + set() {} +}; + +template <typename T> +class multiset : public bidirectional_iterable<iterator<T>> { +public: + multiset() {} +}; + +template <typename key, typename value> +class map : public bidirectional_iterable<iterator<pair<key, value>>> { +public: + map() {} + + iterator<pair<key, value>> find(const key &) {} + const_iterator<iterator<pair<key, value>>> find(const key &) const {} +}; + +template <typename key, typename value> +class multimap : public bidirectional_iterable<iterator<pair<key, value>>> { +public: + multimap() {} +}; + +template <typename T> +class unordered_set : public forward_iterable<iterator<T>> { +public: + unordered_set() {} +}; + +template <typename T> +class unordered_multiset : public forward_iterable<iterator<T>> { +public: + unordered_multiset() {} +}; + +template <typename key, typename value> +class unordered_map : public forward_iterable<iterator<pair<key, value>>> { +public: + unordered_map() {} +}; + +template <typename key, typename value> +class unordered_multimap : public forward_iterable<iterator<pair<key, value>>> { +public: + unordered_multimap() {} +}; + +} // namespace std + +#endif // CONTAINERS_H Index: clang-tidy/modernize/UseAutoCheck.h =================================================================== --- clang-tidy/modernize/UseAutoCheck.h +++ clang-tidy/modernize/UseAutoCheck.h @@ -0,0 +1,35 @@ +//===--- UseAutoCheck.h - clang-tidy-----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_AUTO_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_AUTO_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +class UseAutoCheck : public ClangTidyCheck { +public: + UseAutoCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + + void ReplaceIterators(const DeclStmt *D, ASTContext *Context); + void ReplaceNew(const DeclStmt *D, ASTContext *Context); +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_AUTO_H Index: clang-tidy/modernize/UseAutoCheck.cpp =================================================================== --- clang-tidy/modernize/UseAutoCheck.cpp +++ clang-tidy/modernize/UseAutoCheck.cpp @@ -0,0 +1,373 @@ +//===--- UseAutoCheck.cpp - clang-tidy-------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UseAutoCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang; +using namespace clang::ast_matchers; +using namespace clang::ast_matchers::internal; + +namespace clang { +namespace tidy { +namespace modernize { +namespace { + +const char IteratorDeclStmtId[] = "iterator_decl"; +const char DeclWithNewId[] = "decl_new"; + +/// \brief Matches variable declarations that have explicit initializers that +/// are not initializer lists. +/// +/// Given +/// \code +/// iterator I = Container.begin(); +/// MyType A(42); +/// MyType B{2}; +/// MyType C; +/// \endcode +/// +/// varDecl(hasWrittenNonListInitializer()) maches \c I and \c A but not \c B +/// or \c C. +AST_MATCHER(VarDecl, hasWrittenNonListInitializer) { + const Expr *Init = Node.getAnyInitializer(); + if (!Init) + return false; + + // The following test is based on DeclPrinter::VisitVarDecl() o find if an + // initializer is implicit o not. + bool ImplicitInit = false; + if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) { + if (Construct->isListInitialization()) + return false; + ImplicitInit = Construct->getNumArgs() == 0 || + Construct->getArg(0)->isDefaultArgument(); + } else { + if (Node.getInitStyle() == VarDecl::ListInit) + return false; + } + + return !ImplicitInit; +} + +/// \brief Matches QualTypes that are type sugar for QualTypes that match \c +/// SugarMatcher. +/// +/// Given +/// \code +/// class C {}; +/// typedef C my_type; +/// typedef my_type my_other_type; +/// \endcode +/// +/// qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C")))))) +/// matches \c my_type and \c my_other_type. +AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) { + QualType QT = Node; + while (true) { + if (SugarMatcher.matches(QT, Finder, Builder)) + return true; + + QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext()); + if (NewQT == QT) + break; + QT = NewQT; + } + return false; +} + +/// \brief Matches named declarations that have one of the standard iterator +/// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator. +/// +/// Given +/// \code +/// iterator I; +/// const_iterator CI; +/// \endcode +/// +/// namedDecl(hasStdIteratorName()) matches \c I and \c CI. +AST_MATCHER(NamedDecl, hasStdIteratorName) { + static const char *IteratorNames[] = {"iterator", "reverse_iterator", + "const_iterator", + "const_reverse_iterator"}; + + for (const char *Name : IteratorNames) { + if (hasName(Name).matches(Node, Finder, Builder)) + return true; + } + return false; +} + +/// \brief Matches named declarations that have one of the standard container +/// names. +/// +/// Given +/// \code +/// class vector {}; +/// class forward_list {}; +/// class my_ver{}; +/// \endcode +/// +/// recordDecl(hasStdContainerName()) matches \c vector and \c forward_list +/// but not \c my_vec. +AST_MATCHER(NamedDecl, hasStdContainerName) { + static const char *ContainerNames[] = {"array", "deque", + "forward_list", "list", + "vector", + + "map", "multimap", + "set", "multiset", + + "unordered_map", "unordered_multimap", + "unordered_set", "unordered_multiset", + + "queue", "priority_queue", + "stack"}; + + for (const char *Name : ContainerNames) { + if (hasName(Name).matches(Node, Finder, Builder)) + return true; + } + return false; +} + +/// Matches declarations whose declaration context is the C++ standard library +/// namespace std. +/// +/// Note that inline namespaces are silently ignored during the lookup since +/// both libstdc++ and libc++ are known to use them for versioning purposes. +/// +/// Given: +/// \code +/// namespace ns { +/// struct my_type {}; +/// using namespace std; +/// } +/// +/// using std::vector; +/// using ns:my_type; +/// using ns::list; +/// \code +/// +/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace()))) +/// matches "using std::vector" and "using ns::list". +AST_MATCHER(Decl, isFromStdNamespace) { + const DeclContext *D = Node.getDeclContext(); + + while (D->isInlineNamespace()) + D = D->getParent(); + + if (!D->isNamespace() || !D->getParent()->isTranslationUnit()) + return false; + + const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier(); + + return (Info && Info->isStr("std")); +} + +/// \brief Returns a TypeMatcher that matches typedefs for standard iterators +/// inside records with a standard container name. +TypeMatcher typedefIterator() { + return typedefType( + hasDeclaration(allOf(namedDecl(hasStdIteratorName()), + hasDeclContext(recordDecl(hasStdContainerName(), + isFromStdNamespace()))))); +} + +/// \brief Returns a TypeMatcher that matches records named for standard +/// iterators nested inside records named for standard containers. +TypeMatcher nestedIterator() { + return recordType( + hasDeclaration(allOf(namedDecl(hasStdIteratorName()), + hasDeclContext(recordDecl(hasStdContainerName(), + isFromStdNamespace()))))); +} + +/// \brief Returns a TypeMatcher that matches types declared with using +/// declarations and which name standard iterators for standard containers. +TypeMatcher iteratorFromUsingDeclaration() { + // Types resulting from using declarations are represented by elaboratedType. + return elaboratedType(allOf( + // Unwrap the nested name specifier to test for one of the standard + // containers. + hasQualifier(specifiesType(templateSpecializationType(hasDeclaration( + namedDecl(hasStdContainerName(), isFromStdNamespace()))))), + // the named type is what comes after the final '::' in the type. It + // should name one of the standard iterator names. + namesType( + anyOf(typedefType(hasDeclaration(namedDecl(hasStdIteratorName()))), + recordType(hasDeclaration(namedDecl(hasStdIteratorName()))))))); +} + +/// \brief This matcher returns declaration statements that contain variable +/// declarations with written non-list initializer for standard iterators. +StatementMatcher makeIteratorDeclMatcher() { + return declStmt( + // At least one varDecl should be a child of the declStmt to ensure + // it's a declaration list and avoid matching other declarations, + // e.g. using directives. + has(varDecl()), + unless(has(varDecl(anyOf( + unless(hasWrittenNonListInitializer()), hasType(autoType()), + unless(hasType( + isSugarFor(anyOf(typedefIterator(), nestedIterator(), + iteratorFromUsingDeclaration()))))))))) + .bind(IteratorDeclStmtId); +} + +StatementMatcher makeDeclWithNewMatcher() { + return declStmt(has(varDecl()), + unless(has(varDecl(anyOf( + unless(hasInitializer(ignoringParenImpCasts(newExpr()))), + // FIXME: TypeLoc information is not reliable where CV + // qualifiers are concerned so these types can't be + // handled for now. + hasType(pointerType( + pointee(hasCanonicalType(hasLocalQualifiers())))), + + // FIXME: Handle function pointers. For now we ignore them + // because the replacement replaces the entire type + // specifier source range which includes the identifier. + hasType(pointsTo( + pointsTo(parenType(innerType(functionType())))))))))) + .bind(DeclWithNewId); +} + +} // namespace + +void UseAutoCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(makeIteratorDeclMatcher(), this); + Finder->addMatcher(makeDeclWithNewMatcher(), this); +} + +void UseAutoCheck::ReplaceIterators(const DeclStmt *D, ASTContext *Context) { + for (const auto *Dec : D->decls()) { + const auto *V = cast<VarDecl>(Dec); + const Expr *ExprInit = V->getInit(); + + // Skip expressions with cleanups from the intializer expression. + if (const auto *E = dyn_cast<ExprWithCleanups>(ExprInit)) + ExprInit = E->getSubExpr(); + + const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit); + if (!Construct) + continue; + + // Ensure that the constructor receives a single argument. + if (Construct->getNumArgs() != 1) + return; + + // Drill down to the as-written initializer. + const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts(); + if (E != E->IgnoreConversionOperator()) { + // We hit a conversion operator. Early-out now as they imply an implicit + // conversion from a different type. Could also mean an explicit + // conversion from the same type but that's pretty rare. + return; + } + + if (const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) { + // If we ran into an implicit conversion contructor, can't convert. + // + // FIXME: The following only checks if the constructor can be used + // implicitly, not if it actually was. Cases where the converting + // constructor was used explicitly won't get converted. + if (NestedConstruct->getConstructor()->isConvertingConstructor(false)) + return; + } + if (!Context->hasSameType(V->getType(), E->getType())) + return; + } + + // Get the type location using the first declaration. + const auto *V = cast<VarDecl>(*D->decl_begin()); + + // WARNING: TypeLoc::getSourceRange() will include the identifier for things + // like function pointers. Not a concern since this action only works with + // iterators but something to keep in mind in the future. + + SourceRange Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange()); + auto Diag = diag(Range.getBegin(), "use auto") + << FixItHint::CreateReplacement(Range, "auto"); +} + +void UseAutoCheck::ReplaceNew(const DeclStmt *D, ASTContext *Context) { + const auto *FirstDecl = cast<VarDecl>(*D->decl_begin()); + // Ensure that there is at least one VarDecl within the DeclStmt. + if (!FirstDecl) + return; + + const QualType FirstDeclType = FirstDecl->getType().getCanonicalType(); + + std::vector<SourceLocation> StarLocations; + for (const auto *Dec : D->decls()) { + const auto *V = cast<VarDecl>(Dec); + // Ensure that every DeclStmt child is a VarDecl. + if (!V) + return; + + const auto *NewExpr = cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts()); + // Ensure that every VarDecl has a CXXNewExpr initializer. + if (!NewExpr) + return; + + // If VarDecl and Initializer have mismatching unqualified types. + if (!Context->hasSameUnqualifiedType(V->getType(), NewExpr->getType())) + return; + + // Remove explicitly written '*' from declarations where there's more than + // one declaration in the declaration list. + if (Dec == *D->decl_begin()) + continue; + + // All subsequent declarations should match the same non-decorated type. + if (FirstDeclType != V->getType().getCanonicalType()) + return; + + auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>(); + while (!Q.isNull()) { + StarLocations.push_back(Q.getStarLoc()); + Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>(); + } + } + + // FIXME: There is, however, one case we can address: when the VarDecl pointee + // is the same as the initializer, just more CV-qualified. However, TypeLoc + // information is not reliable where CV qualifiers are concerned so we can't + // do anything about this case for now. + SourceRange Range( + FirstDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange()); + auto Diag = diag(Range.getBegin(), "use auto"); + + // Space after 'auto' to handle cases where the '*' in the pointer type is + // next to the identifier. This avoids changing 'int *p' into 'autop'. + Diag << FixItHint::CreateReplacement(Range, "auto "); + + // Remove '*' from declarations using the saved star locations. + for (const auto &Loc : StarLocations) { + Diag << FixItHint::CreateReplacement(Loc, ""); + } +} + +void UseAutoCheck::check(const MatchFinder::MatchResult &Result) { + const DeclStmt *Dec; + if ((Dec = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId))) { + ReplaceIterators(Dec, Result.Context); + } else { + Dec = Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId); + assert(Dec && "Bad Callback. No node provided."); + ReplaceNew(Dec, Result.Context); + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "LoopConvertCheck.h" #include "PassByValueCheck.h" +#include "UseAutoCheck.h" #include "UseNullptrCheck.h" using namespace clang::ast_matchers; @@ -25,6 +26,7 @@ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert"); CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value"); + CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto"); CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr"); } Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -5,6 +5,7 @@ LoopConvertUtils.cpp ModernizeTidyModule.cpp PassByValueCheck.cpp + UseAutoCheck.cpp UseNullptrCheck.cpp LINK_LIBS
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits