mnbvmar created this revision.
mnbvmar added a reviewer: alexfh.
mnbvmar added subscribers: krystyna, sbarzowski, Prazek, staronj, cfe-commits.
This implements unnecessary-mutable check. It's still bug-prone and might
produce false positives, so all suggestions are welcome.
http://reviews.llvm.org/D20053
Files:
clang-tidy/misc/CMakeLists.txt
clang-tidy/misc/MiscTidyModule.cpp
clang-tidy/misc/UnnecessaryMutableCheck.cpp
clang-tidy/misc/UnnecessaryMutableCheck.h
docs/clang-tidy/checks/list.rst
docs/clang-tidy/checks/misc-unnecessary-mutable.rst
test/clang-tidy/misc-unnecessary-mutable.cpp
Index: test/clang-tidy/misc-unnecessary-mutable.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/misc-unnecessary-mutable.cpp
@@ -0,0 +1,354 @@
+// RUN: %check_clang_tidy %s misc-unnecessary-mutable %t
+
+struct NothingMutable {
+ int field1;
+ unsigned field2;
+ const int field3;
+ volatile float field4;
+
+ NothingMutable(int a1, unsigned a2, int a3, float a4) : field1(a1), field2(a2), field3(a3), field4(a4) {}
+
+ void doSomething() {
+ field1 = 1;
+ field2 = 2;
+ field4 = 4;
+ }
+};
+
+struct NoMethods {
+ int field1;
+ mutable unsigned field2; // These cannot be fixed; they're public
+ const int field3;
+ mutable volatile NothingMutable field4;
+};
+
+class NoMethodsClass {
+public:
+ int field1;
+ mutable unsigned field2;
+
+private:
+ const int field3;
+ mutable volatile NothingMutable field4;
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: 'mutable' modifier is unnecessary for field 'field4' [misc-unnecessary-mutable]
+ // CHECK-FIXES: {{^ }}volatile NothingMutable field4;
+};
+
+struct PrivateInStruct {
+private:
+ mutable volatile unsigned long long blah;
+ // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: 'mutable' modifier is unnecessary for field 'blah' {{..}}
+ // CHECK-FIXES: {{^ }}volatile unsigned long long blah;
+};
+
+union PrivateInUnion {
+public:
+ int someField;
+
+private:
+ mutable char otherField;
+};
+
+class UnusedVar {
+private:
+ mutable int x __attribute__((unused));
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'mutable' modifier is unnecessary for field 'x' {{..}}
+ // CHECK-FIXES: {{^ }}int x __attribute__((unused));
+};
+
+class NoConstMethodsClass {
+public:
+ int field1;
+ mutable unsigned field2;
+
+ NoConstMethodsClass() : field2(42), field3(9), field4(NothingMutable(1, 2, 3, 4)) {}
+
+ void doSomething() {
+ field2 = 8;
+ field1 = 99;
+ field4.doSomething();
+ }
+
+private:
+ const int field3;
+ mutable NothingMutable field4;
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 'mutable' modifier is unnecessary for field 'field4' {{..}}
+ // CHECK-FIXES: {{^ }}NothingMutable field4;
+};
+
+class ConstMethods {
+private:
+ mutable int field1, field2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 'mutable' modifier is unnecessary for field 'field2' {{..}}
+ mutable int incr, decr, set, mul, constArg1, constArg2, constRef, ref1, ref2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: 'mutable' modifier is unnecessary for field 'constArg1' {{..}}
+ // CHECK-MESSAGES: :[[@LINE-2]]:48: warning: 'mutable' modifier is unnecessary for field 'constArg2' {{..}}
+ // CHECK-MESSAGES: :[[@LINE-3]]:59: warning: 'mutable' modifier is unnecessary for field 'constRef' {{..}}
+
+ void takeArg(int x) const { x *= 4; }
+ int takeConstRef(const int &x) const { return x + 99; }
+ void takeRef(int &) const {}
+
+ template <typename... Args>
+ void takeArgs(Args... args) const {}
+ template <typename... Args>
+ void takeArgRefs(Args &... args) const {}
+
+public:
+ void doSomething() const {
+ field1 = field2;
+ }
+
+ void doOtherThing() const {
+ incr++;
+ decr--;
+ set = 42;
+ mul *= 3;
+ takeArg(constArg1);
+ takeConstRef(constRef);
+ takeRef(ref1);
+ takeArgs(constArg1, constArg2);
+ takeArgRefs(ref1, ref2);
+ }
+};
+
+class NonFinalClass {
+public:
+ mutable int fPublic;
+
+protected:
+ mutable int fProtected;
+
+private:
+ mutable int fPrivate;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'mutable' modifier is unnecessary for field 'fPrivate' {{..}}
+ // CHECK-FIXES: {{^ }}int fPrivate;
+};
+
+class FinalClass final {
+public:
+ mutable int fPublic;
+
+protected:
+ mutable int fProtected;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'mutable' modifier is unnecessary for field 'fProtected' {{..}}
+ // CHECK-FIXES: {{^ }}int fProtected;
+
+private:
+ mutable int fPrivate;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'mutable' modifier is unnecessary for field 'fPrivate' {{..}}
+ // CHECK-FIXES: {{^ }}int fPrivate;
+};
+
+class NotAllFuncsKnown {
+ void doSomething();
+ void doSomethingConst() const {}
+
+private:
+ mutable int field;
+ // Can't be fixed. We don't know if doSomething() doesn't declare a *const* NotAllFuncsKnown instance
+ // and then modify 'field' field.
+};
+
+class NotAllConstFuncsKnown {
+ void doSomething() {}
+ void doSomethingConst() const;
+ void doOtherConst() const {}
+
+private:
+ mutable int field;
+};
+
+class ConstFuncOutside {
+ void doSomething();
+ void doSomethingConst() const;
+ void doOtherConst() const {}
+
+private:
+ mutable int field;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'mutable' modifier is unnecessary for field 'field' {{..}}
+ // CHECK-FIXES: {{^ }}int field;
+};
+
+void ConstFuncOutside::doSomethingConst() const {}
+
+// We can't really see if mutable is necessary or not.
+template <typename T>
+class TemplatedClass {
+public:
+ void doSomething() {
+ a = b = c;
+ }
+
+ void doSomethingConst() const {
+ a = b = c;
+ }
+
+private:
+ mutable int a, b, c;
+};
+
+TemplatedClass<int> TemplatedClassInt;
+
+class ClassWithTemplates {
+public:
+ void doSomethingConst() const {
+ a = b;
+ }
+
+ // Here, we can see that.
+ template <typename T>
+ void doOtherConst() const {
+ b = c;
+ }
+
+private:
+ mutable int a, b, c, d;
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 'mutable' modifier is unnecessary for field 'c' {{..}}
+ // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: 'mutable' modifier is unnecessary for field 'd' {{..}}
+};
+
+class ImplicitCast {
+public:
+ void doSomethingConst() const {
+ a = b;
+ }
+
+private:
+ mutable int a;
+ mutable double b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'mutable' modifier is unnecessary for field 'b' {{..}}
+ // CHECK_FIXES: {{^ }}double b;
+};
+
+struct MutableNotFirst {
+private:
+ long mutable long abc = 42;
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 'mutable' modifier is unnecessary for field 'abc' {{..}}
+ // CHECK_FIXES: {{^ }}long long abc;
+ long long mutable bca;
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 'mutable' modifier is unnecessary for field 'bca' {{..}}
+ // CHECK_FIXES: {{^ }}long long bca;
+ int mutable ca;
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'mutable' modifier is unnecessary for field 'ca' {{..}}
+ // CHECK_FIXES: {{^ }}int ca;
+};
+
+// Fails for now.
+/*
+void change(const int &a) {
+ const_cast<int&>(a) = 42;
+}
+
+struct Change {
+ void changeMember() const {
+ change(m);
+ const int& x = otherM;
+ const_cast<int&>(x) = 33;
+
+ const_cast<int&>(anotherM) = -4;
+ }
+private:
+ mutable int m;
+ mutable int otherM;
+ mutable int anotherM;
+};
+*/
+
+class StrangeClass {
+public:
+ void foo() {}
+};
+
+void EvilFunction(int &a) {}
+void EvilFunction(const int &a) {}
+
+class JustClass {
+public:
+ JustClass() {}
+
+ void foo() {
+ MutableClass.foo();
+ }
+
+ void foo() const;
+ void evilCaller() const;
+
+private:
+ mutable StrangeClass MutableClass; // Must stay mutable (because of foo func below)
+ mutable int MutableInt; // Must stay mutable
+};
+
+void JustClass::foo() const {
+ MutableClass.foo();
+}
+
+void JustClass::evilCaller() const {
+ EvilFunction(MutableInt);
+}
+
+class AnotherStrangeClass {
+public:
+ // Example of non-const method which requires that MutableInt should stay mutable.
+ void strangeFoo() {
+ const AnotherStrangeClass *ConstThis = this;
+ EvilFunction(ConstThis->MutableInt);
+ }
+
+private:
+ mutable int MutableInt; // must stay mutable
+};
+
+class ClassWithStrangeConstructor {
+public:
+ ClassWithStrangeConstructor() {
+ const ClassWithStrangeConstructor *ConstThis = this;
+ EvilFunction(ConstThis->MutableInt);
+ }
+
+private:
+ mutable int MutableInt; // must stay mutable
+};
+
+class ClassWithStrangeDestructor {
+public:
+ ~ClassWithStrangeDestructor() {
+ const ClassWithStrangeDestructor *ConstThis = this;
+ EvilFunction(ConstThis->MutableInt);
+ }
+
+private:
+ mutable int MutableInt; // must stay mutable
+};
+
+// Don't touch friends or they'll hurt you.
+class ClassWithFriends {
+public:
+ friend void someFriend(ClassWithFriends *);
+
+private:
+ mutable int MutableInt; // must stay mutable
+};
+
+void someFriend(ClassWithFriends *Class) {
+ const ClassWithFriends *ConstClass = Class;
+ EvilFunction(ConstClass->MutableInt);
+}
+
+class ClassWithClassFriends {
+public:
+ friend class ClassFriend;
+
+private:
+ mutable int MutableInt; // must stay mutable
+};
+
+class ClassFriend {
+public:
+ static void foo(const ClassWithClassFriends *Class) {
+ EvilFunction(Class->MutableInt);
+ }
+
+private:
+ mutable int m;
+};
Index: docs/clang-tidy/checks/misc-unnecessary-mutable.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/misc-unnecessary-mutable.rst
@@ -0,0 +1,49 @@
+.. title:: clang-tidy - misc-unnecessary-mutable
+
+misc-unnecessary-mutable
+========================
+
+The tool tries to find situations where ``mutable`` might be unnecessary
+(i.e. each use of the mutable field on the const object is constant).
+
+.. code-block:: c++
+ class SomeClass {
+ public:
+ void foo() {
+ field2 = 42;
+ }
+
+ void bar() const {
+ field3 = 99;
+ }
+
+ // Should stay mutable. Everyone can change it.
+ mutable int field1;
+
+ private:
+ // Might lose 'mutable'. It's never modified in const object.
+ mutable int field2;
+
+ // Should stay mutable; bar() changes it even though 'this' is const.
+ mutable int field3;
+ };
+
+It also can automatically remove ``mutable`` keyword. For now, it is done
+only if non-necessarily mutable field is the only declaration in the statement.
+
+Please note that there is a possibility that false-positives occur. For example,
+one can use ``const_cast``:
+
+.. code-block:: c++
+ class AnotherClass {
+ public:
+ void foo() const {
+ const int& y = x;
+ const_cast<int&>(y) = 42;
+ }
+
+ private:
+ mutable int x; // SHOULD stay mutable. For now, it does not.
+ };
+
+The tool should be thus used with care.
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -85,6 +85,7 @@
misc-throw-by-value-catch-by-reference
misc-undelegated-constructor
misc-uniqueptr-reset-release
+ misc-unnecessary-mutable
misc-unused-alias-decls
misc-unused-parameters
misc-unused-raii
Index: clang-tidy/misc/UnnecessaryMutableCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/misc/UnnecessaryMutableCheck.h
@@ -0,0 +1,43 @@
+//===--- UnnecessaryMutableCheck.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_MISC_UNNECESSARY_MUTABLE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNNECESSARY_MUTABLE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Searches for and removes unnecessary 'mutable' keywords (that is, for
+/// fields that cannot be possibly changed in const methods).
+///
+/// Example:
+///
+/// class MyClass {
+/// mutable int field; // will change into "int field;"
+/// };
+///
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-unnecessary-mutable.html
+class UnnecessaryMutableCheck : public ClangTidyCheck {
+public:
+ UnnecessaryMutableCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNNECESSARY_MUTABLE_H
Index: clang-tidy/misc/UnnecessaryMutableCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/misc/UnnecessaryMutableCheck.cpp
@@ -0,0 +1,223 @@
+//===--- UnnecessaryMutableCheck.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 "UnnecessaryMutableCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void UnnecessaryMutableCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ fieldDecl(anyOf(isPrivate(), allOf(isProtected(),
+ hasParent(cxxRecordDecl(isFinal())))),
+ unless(anyOf(isImplicit(), isInstantiated(),
+ hasParent(cxxRecordDecl(isUnion())))))
+ .bind("field"),
+ this);
+}
+
+class FieldUseVisitor : public RecursiveASTVisitor<FieldUseVisitor> {
+public:
+ explicit FieldUseVisitor(FieldDecl *SoughtField)
+ : SoughtField(SoughtField), FoundNonConstUse(false) {}
+
+ void RunSearch(Decl *Declaration) {
+ auto *Body = Declaration->getBody();
+ ParMap = new ParentMap(Body);
+ TraverseStmt(Body);
+ delete ParMap;
+ }
+
+ bool VisitExpr(Expr *GenericExpr) {
+ MemberExpr *Expression = dyn_cast<MemberExpr>(GenericExpr);
+ if (Expression == nullptr)
+ return true;
+
+ if (Expression->getMemberDecl() != SoughtField)
+ return true;
+
+ // Check if expr is a member of const thing.
+ bool IsConstObj = false;
+ for (auto *ChildStmt : Expression->children()) {
+ Expr *ChildExpr = dyn_cast<Expr>(ChildStmt);
+ if (ChildExpr) {
+ IsConstObj |= ChildExpr->getType().isConstQualified();
+
+ // If member expression dereferences, we need to check
+ // whether pointer type is const.
+ if (Expression->isArrow()) {
+ auto &WrappedType = *ChildExpr->getType();
+ if (!WrappedType.isPointerType()) {
+ // It's something weird. Just to be sure, assume we're const.
+ IsConstObj = true;
+ } else {
+ IsConstObj |= WrappedType.getPointeeType().isConstQualified();
+ }
+ }
+ }
+ }
+
+ // If it's not, mutableness changes nothing.
+ if (!IsConstObj)
+ return true;
+
+ // By a const operation on a member expression we mean a MemberExpr
+ // whose parent is ImplicitCastExpr to rvalue or something constant.
+ bool HasRvalueCast = false;
+ bool HasConstCast = false;
+ if (ParMap->hasParent(Expression)) {
+ const auto *Cast =
+ dyn_cast<ImplicitCastExpr>(ParMap->getParent(Expression));
+ if (Cast != nullptr) {
+ HasRvalueCast = Cast->getCastKind() == CastKind::CK_LValueToRValue;
+ HasConstCast = Cast->getType().isConstQualified();
+ }
+ }
+
+ if (!HasRvalueCast && !HasConstCast) {
+ FoundNonConstUse = true;
+ return false;
+ }
+
+ return true;
+ }
+
+ bool IsNonConstUseFound() const { return FoundNonConstUse; }
+
+private:
+ FieldDecl *SoughtField;
+ ParentMap *ParMap;
+ bool FoundNonConstUse;
+};
+
+class ClassMethodVisitor : public RecursiveASTVisitor<ClassMethodVisitor> {
+public:
+ ClassMethodVisitor(ASTContext &Context, FieldDecl *SoughtField)
+ : SM(Context.getSourceManager()), SoughtField(SoughtField),
+ NecessaryMutable(false) {}
+
+ bool VisitDecl(Decl *GenericDecl) {
+ // As for now, we can't track friends.
+ if (dyn_cast<FriendDecl>(GenericDecl)) {
+ NecessaryMutable = true;
+ return false;
+ }
+
+ FunctionDecl *Declaration = dyn_cast<FunctionDecl>(GenericDecl);
+
+ if (Declaration == nullptr)
+ return true;
+
+ // All decls need their definitions in main file.
+ if (!Declaration->hasBody() ||
+ !SM.isInMainFile(Declaration->getBody()->getLocStart())) {
+ NecessaryMutable = true;
+ return false;
+ }
+
+ FieldUseVisitor FieldVisitor(SoughtField);
+ FieldVisitor.RunSearch(Declaration);
+ if (FieldVisitor.IsNonConstUseFound()) {
+ NecessaryMutable = true;
+ return false;
+ }
+
+ return true;
+ }
+
+ bool IsMutableNecessary() const { return NecessaryMutable; }
+
+private:
+ SourceManager &SM;
+ FieldDecl *SoughtField;
+ bool NecessaryMutable;
+};
+
+// Checks if 'mutable' keyword can be removed; for now; we do it only if
+// it is the only declaration in a declaration chain.
+static bool CheckRemoval(SourceManager &SM, const SourceLocation &LocStart,
+ const SourceLocation &LocEnd, ASTContext &Context,
+ SourceRange &ResultRange) {
+
+ FileID FID = SM.getFileID(LocEnd);
+ llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, LocEnd);
+ Lexer DeclLexer(SM.getLocForStartOfFile(FID), Context.getLangOpts(),
+ Buffer->getBufferStart(), SM.getCharacterData(LocStart),
+ Buffer->getBufferEnd());
+
+ Token DeclToken;
+ bool result = false;
+
+ while (!DeclLexer.LexFromRawLexer(DeclToken)) {
+
+ if (DeclToken.getKind() == tok::TokenKind::semi) {
+ break;
+ }
+
+ if (DeclToken.getKind() == tok::TokenKind::comma) {
+ return false;
+ }
+
+ if (DeclToken.isOneOf(tok::TokenKind::identifier,
+ tok::TokenKind::raw_identifier)) {
+ auto TokenStr = DeclToken.getRawIdentifier().str();
+
+ // "mutable" cannot be used in any other way than to mark mutableness
+ if (TokenStr == "mutable") {
+ ResultRange =
+ SourceRange(DeclToken.getLocation(), DeclToken.getEndLoc());
+ result = true;
+ }
+ }
+ }
+
+ assert(result && "No mutable found, weird");
+
+ return result;
+}
+
+void UnnecessaryMutableCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MD = Result.Nodes.getNodeAs<FieldDecl>("field");
+ const auto *ClassMatch = dyn_cast<CXXRecordDecl>(MD->getParent());
+ auto &Context = *Result.Context;
+ auto &SM = *Result.SourceManager;
+
+ if (!MD->getDeclName() || ClassMatch->isDependentContext() ||
+ !MD->isMutable())
+ return;
+
+ ClassMethodVisitor Visitor(Context, const_cast<FieldDecl *>(MD));
+ Visitor.TraverseDecl(const_cast<CXXRecordDecl *>(ClassMatch));
+
+ if (Visitor.IsMutableNecessary())
+ return;
+
+ auto Diag =
+ diag(MD->getLocation(), "'mutable' modifier is unnecessary for field %0")
+ << MD->getDeclName();
+
+ SourceRange RemovalRange;
+
+ if (CheckRemoval(SM, MD->getLocStart(), MD->getLocEnd(), Context,
+ RemovalRange)) {
+ Diag << FixItHint::CreateRemoval(RemovalRange);
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tidy/misc/MiscTidyModule.cpp
@@ -45,6 +45,7 @@
#include "ThrowByValueCatchByReferenceCheck.h"
#include "UndelegatedConstructor.h"
#include "UniqueptrResetReleaseCheck.h"
+#include "UnnecessaryMutableCheck.h"
#include "UnusedAliasDeclsCheck.h"
#include "UnusedParametersCheck.h"
#include "UnusedRAIICheck.h"
@@ -126,6 +127,8 @@
"misc-undelegated-constructor");
CheckFactories.registerCheck<UniqueptrResetReleaseCheck>(
"misc-uniqueptr-reset-release");
+ CheckFactories.registerCheck<UnnecessaryMutableCheck>(
+ "misc-unnecessary-mutable");
CheckFactories.registerCheck<UnusedAliasDeclsCheck>(
"misc-unused-alias-decls");
CheckFactories.registerCheck<UnusedParametersCheck>(
Index: clang-tidy/misc/CMakeLists.txt
===================================================================
--- clang-tidy/misc/CMakeLists.txt
+++ clang-tidy/misc/CMakeLists.txt
@@ -37,6 +37,7 @@
ThrowByValueCatchByReferenceCheck.cpp
UndelegatedConstructor.cpp
UniqueptrResetReleaseCheck.cpp
+ UnnecessaryMutableCheck.cpp
UnusedAliasDeclsCheck.cpp
UnusedParametersCheck.cpp
UnusedRAIICheck.cpp
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits