[prev in list] [next in list] [prev in thread] [next in thread]
List: cfe-commits
Subject: Re: [PATCH] Refactor: Move remove-cstr-calls from a standalone executable to a clang-tidy check
From: Richard <legalize () xmission ! com>
Date: 2015-02-28 20:50:49
Message-ID: 50d04bcef9295855c3095dcae8e67cd4 () localhost ! localdomain
[Download RAW message or body]
Use isa<> instead of dyn_cast<>.
Move the check from namespace clang::tidy into namespace clang::tidy::reada=
bility
http://reviews.llvm.org/D7318
Files:
CMakeLists.txt
clang-tidy/readability/CMakeLists.txt
clang-tidy/readability/ReadabilityTidyModule.cpp
clang-tidy/readability/RemoveCStrCall.cpp
clang-tidy/readability/RemoveCStrCall.h
remove-cstr-calls/CMakeLists.txt
remove-cstr-calls/Makefile
remove-cstr-calls/RemoveCStrCalls.cpp
test/CMakeLists.txt
test/clang-tidy/readability-remove-cstr-call.cpp
test/remove-cstr-calls/basic.cpp
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
["D7318.20937.patch" (text/x-patch)]
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -2,7 +2,6 @@
add_subdirectory(clang-modernize)
add_subdirectory(clang-rename)
add_subdirectory(modularize)
-add_subdirectory(remove-cstr-calls)
if(CLANG_ENABLE_STATIC_ANALYZER)
add_subdirectory(clang-tidy)
endif()
Index: clang-tidy/readability/CMakeLists.txt
===================================================================
--- clang-tidy/readability/CMakeLists.txt
+++ clang-tidy/readability/CMakeLists.txt
@@ -8,6 +8,7 @@
NamespaceCommentCheck.cpp
ReadabilityTidyModule.cpp
RedundantSmartptrGet.cpp
+ RemoveCStrCall.cpp
ShrinkToFitCheck.cpp
LINK_LIBS
Index: clang-tidy/readability/ReadabilityTidyModule.cpp
===================================================================
--- clang-tidy/readability/ReadabilityTidyModule.cpp
+++ clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -15,6 +15,7 @@
#include "ElseAfterReturnCheck.h"
#include "FunctionSize.h"
#include "RedundantSmartptrGet.h"
+#include "RemoveCStrCall.h"
#include "ShrinkToFitCheck.h"
namespace clang {
@@ -34,6 +35,8 @@
"readability-function-size");
CheckFactories.registerCheck<RedundantSmartptrGet>(
"readability-redundant-smartptr-get");
+ CheckFactories.registerCheck<RemoveCStrCall>(
+ "readability-remove-cstr-call");
CheckFactories.registerCheck<ShrinkToFitCheck>(
"readability-shrink-to-fit");
}
Index: clang-tidy/readability/RemoveCStrCall.cpp
===================================================================
--- /dev/null
+++ clang-tidy/readability/RemoveCStrCall.cpp
@@ -0,0 +1,145 @@
+//===- RemoveCStrCall.cpp - Check for redundant c_str call ----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a check for redundant calls of c_str() on strings.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RemoveCStrCall.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+namespace {
+
+template <typename T>
+StringRef getText(const ast_matchers::MatchFinder::MatchResult &Result, T const &Node) {
+ return Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Node.getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+}
+
+// Return true if expr needs to be put in parens when it is an argument of a
+// prefix unary operator, e.g. when it is a binary or ternary operator
+// syntactically.
+bool needParensAfterUnaryOperator(const Expr &ExprNode) {
+ if (isa<clang::BinaryOperator>(&ExprNode) ||
+ isa<clang::ConditionalOperator>(&ExprNode)) {
+ return true;
+ }
+ if (const CXXOperatorCallExpr *op =
+ dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
+ return op->getNumArgs() == 2 &&
+ op->getOperator() != OO_PlusPlus &&
+ op->getOperator() != OO_MinusMinus &&
+ op->getOperator() != OO_Call &&
+ op->getOperator() != OO_Subscript;
+ }
+ return false;
+}
+
+// Format a pointer to an expression: prefix with '*' but simplify
+// when it already begins with '&'. Return empty string on failure.
+std::string formatDereference(const ast_matchers::MatchFinder::MatchResult &Result,
+ const Expr &ExprNode) {
+ if (const auto Op = dyn_cast<clang::UnaryOperator>(&ExprNode)) {
+ if (Op->getOpcode() == UO_AddrOf) {
+ // Strip leading '&'.
+ return getText(Result, *Op->getSubExpr()->IgnoreParens());
+ }
+ }
+ const std::string Text = getText(Result, ExprNode);
+ if (Text.empty()) return std::string();
+ // Add leading '*'.
+ if (needParensAfterUnaryOperator(ExprNode)) {
+ return std::string("*(") + Text + ")";
+ }
+ return std::string("*") + Text;
+}
+
+const char *const StringConstructor =
+ "::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
+ "::basic_string";
+
+const char *const StringCStrMethod =
+ "::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
+ "::c_str";
+
+} // end namespace
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void RemoveCStrCall::registerMatchers(ast_matchers::MatchFinder *Finder)
+{
+ Finder->addMatcher(
+ constructExpr(
+ hasDeclaration(methodDecl(hasName(StringConstructor))),
+ argumentCountIs(2),
+ // The first argument must have the form x.c_str() or p->c_str()
+ // where the method is string::c_str(). We can use the copy
+ // constructor of string instead (or the compiler might share
+ // the string object).
+ hasArgument(
+ 0,
+ id("call", memberCallExpr(
+ callee(id("member", memberExpr())),
+ callee(methodDecl(hasName(StringCStrMethod))),
+ on(id("arg", expr()))))),
+ // The second argument is the alloc object which must not be
+ // present explicitly.
+ hasArgument(
+ 1,
+ defaultArgExpr())),
+ this);
+ Finder->addMatcher(
+ constructExpr(
+ // Implicit constructors of these classes are overloaded
+ // wrt. string types and they internally make a StringRef
+ // referring to the argument. Passing a string directly to
+ // them is preferred to passing a char pointer.
+ hasDeclaration(methodDecl(anyOf(
+ hasName("::llvm::StringRef::StringRef"),
+ hasName("::llvm::Twine::Twine")))),
+ argumentCountIs(1),
+ // The only argument must have the form x.c_str() or p->c_str()
+ // where the method is string::c_str(). StringRef also has
+ // a constructor from string which is more efficient (avoids
+ // strlen), so we can construct StringRef from the string
+ // directly.
+ hasArgument(
+ 0,
+ id("call", memberCallExpr(
+ callee(id("member", memberExpr())),
+ callee(methodDecl(hasName(StringCStrMethod))),
+ on(id("arg", expr())))))),
+ this);
+}
+
+void RemoveCStrCall::check(const ast_matchers::MatchFinder::MatchResult &Result)
+{
+ const CallExpr *Call = Result.Nodes.getStmtAs<CallExpr>("call");
+ const Expr *Arg = Result.Nodes.getStmtAs<Expr>("arg");
+ const bool Arrow = Result.Nodes.getStmtAs<MemberExpr>("member")->isArrow();
+ // Replace the "call" node with the "arg" node, prefixed with '*'
+ // if the call was using '->' rather than '.'.
+ const std::string ArgText = Arrow ?
+ formatDereference(Result, *Arg) :
+ static_cast<std::string>(getText(Result, *Arg));
+ if (ArgText.empty()) return;
+
+ diag(Call->getLocStart(), "redundant call to `c_str()`")
+ << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/readability/RemoveCStrCall.h
===================================================================
--- /dev/null
+++ clang-tidy/readability/RemoveCStrCall.h
@@ -0,0 +1,32 @@
+//===--- RemoveCStrCall.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_READABILITY_REMOVE_C_STR_CALL_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REMOVE_C_STR_CALL_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// \brief Finds unnecessary calls to std::string::c_str().
+class RemoveCStrCall : public ClangTidyCheck {
+public:
+ RemoveCStrCall(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REMOVE_C_STR_CALL_H
Index: remove-cstr-calls/CMakeLists.txt
===================================================================
--- remove-cstr-calls/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-set(LLVM_LINK_COMPONENTS
- Support
- )
-
-add_clang_executable(remove-cstr-calls
- RemoveCStrCalls.cpp
- )
-
-target_link_libraries(remove-cstr-calls
- clangAST
- clangASTMatchers
- clangBasic
- clangFrontend
- clangLex
- clangTooling
- clangToolingCore
- )
Index: remove-cstr-calls/Makefile
===================================================================
--- remove-cstr-calls/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-##===- tools/extra/remove-cstr-calls/Makefile --------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-CLANG_LEVEL := ../../..
-
-TOOLNAME = remove-cstr-calls
-NO_INSTALL = 1
-
-# No plugins, optimize startup time.
-TOOL_NO_EXPORTS = 1
-
-include $(CLANG_LEVEL)/../../Makefile.config
-LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option
-USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
- clangToolingCore.a clangRewriteFrontend.a clangRewrite.a \
- clangParse.a clangSema.a clangAnalysis.a \
- clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
-
-include $(CLANG_LEVEL)/Makefile
Index: remove-cstr-calls/RemoveCStrCalls.cpp
===================================================================
--- remove-cstr-calls/RemoveCStrCalls.cpp
+++ /dev/null
@@ -1,237 +0,0 @@
-//===- examples/Tooling/RemoveCStrCalls.cpp - Redundant c_str call removal ===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements a tool that prints replacements that remove redundant
-// calls of c_str() on strings.
-//
-// Usage:
-// remove-cstr-calls <cmake-output-dir> <file1> <file2> ...
-//
-// Where <cmake-output-dir> is a CMake build directory in which a file named
-// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
-// CMake to get this output).
-//
-// <file1> ... specify the paths of files in the CMake source tree. This path
-// is looked up in the compile command database. If the path of a file is
-// absolute, it needs to point into CMake's source tree. If the path is
-// relative, the current working directory needs to be in the CMake source
-// tree and the file must be in a subdirectory of the current working
-// directory. "./" prefixes in the relative files will be automatically
-// removed, but the rest of a relative path must be a suffix of a path in
-// the compile command line database.
-//
-// For example, to use remove-cstr-calls on all files in a subtree of the
-// source tree, use:
-//
-// /path/in/subtree $ find . -name '*.cpp'|
-// xargs remove-cstr-calls /path/to/build
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Frontend/FrontendActions.h"
-#include "clang/Lex/Lexer.h"
-#include "clang/Tooling/CompilationDatabase.h"
-#include "clang/Tooling/Refactoring.h"
-#include "clang/Tooling/Tooling.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/Path.h"
-#include "llvm/Support/Signals.h"
-#include "llvm/Support/raw_ostream.h"
-#include <system_error>
-
-using namespace clang;
-using namespace clang::ast_matchers;
-using namespace llvm;
-using clang::tooling::newFrontendActionFactory;
-using clang::tooling::Replacement;
-using clang::tooling::CompilationDatabase;
-
-// FIXME: Pull out helper methods in here into more fitting places.
-
-// Returns the text that makes up 'node' in the source.
-// Returns an empty string if the text cannot be found.
-template <typename T>
-static std::string getText(const SourceManager &SourceManager, const T &Node) {
- SourceLocation StartSpellingLocation =
- SourceManager.getSpellingLoc(Node.getLocStart());
- SourceLocation EndSpellingLocation =
- SourceManager.getSpellingLoc(Node.getLocEnd());
- if (!StartSpellingLocation.isValid() || !EndSpellingLocation.isValid()) {
- return std::string();
- }
- bool Invalid = true;
- const char *Text =
- SourceManager.getCharacterData(StartSpellingLocation, &Invalid);
- if (Invalid) {
- return std::string();
- }
- std::pair<FileID, unsigned> Start =
- SourceManager.getDecomposedLoc(StartSpellingLocation);
- std::pair<FileID, unsigned> End =
- SourceManager.getDecomposedLoc(Lexer::getLocForEndOfToken(
- EndSpellingLocation, 0, SourceManager, LangOptions()));
- if (Start.first != End.first) {
- // Start and end are in different files.
- return std::string();
- }
- if (End.second < Start.second) {
- // Shuffling text with macros may cause this.
- return std::string();
- }
- return std::string(Text, End.second - Start.second);
-}
-
-// Return true if expr needs to be put in parens when it is an argument of a
-// prefix unary operator, e.g. when it is a binary or ternary operator
-// syntactically.
-static bool needParensAfterUnaryOperator(const Expr &ExprNode) {
- if (dyn_cast<clang::BinaryOperator>(&ExprNode) ||
- dyn_cast<clang::ConditionalOperator>(&ExprNode)) {
- return true;
- }
- if (const CXXOperatorCallExpr *op =
- dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
- return op->getNumArgs() == 2 &&
- op->getOperator() != OO_PlusPlus &&
- op->getOperator() != OO_MinusMinus &&
- op->getOperator() != OO_Call &&
- op->getOperator() != OO_Subscript;
- }
- return false;
-}
-
-// Format a pointer to an expression: prefix with '*' but simplify
-// when it already begins with '&'. Return empty string on failure.
-static std::string formatDereference(const SourceManager &SourceManager,
- const Expr &ExprNode) {
- if (const clang::UnaryOperator *Op =
- dyn_cast<clang::UnaryOperator>(&ExprNode)) {
- if (Op->getOpcode() == UO_AddrOf) {
- // Strip leading '&'.
- return getText(SourceManager, *Op->getSubExpr()->IgnoreParens());
- }
- }
- const std::string Text = getText(SourceManager, ExprNode);
- if (Text.empty()) return std::string();
- // Add leading '*'.
- if (needParensAfterUnaryOperator(ExprNode)) {
- return std::string("*(") + Text + ")";
- }
- return std::string("*") + Text;
-}
-
-namespace {
-class FixCStrCall : public ast_matchers::MatchFinder::MatchCallback {
- public:
- FixCStrCall(tooling::Replacements *Replace)
- : Replace(Replace) {}
-
- virtual void run(const ast_matchers::MatchFinder::MatchResult &Result) {
- const CallExpr *Call =
- Result.Nodes.getStmtAs<CallExpr>("call");
- const Expr *Arg =
- Result.Nodes.getStmtAs<Expr>("arg");
- const bool Arrow =
- Result.Nodes.getStmtAs<MemberExpr>("member")->isArrow();
- // Replace the "call" node with the "arg" node, prefixed with '*'
- // if the call was using '->' rather than '.'.
- const std::string ArgText = Arrow ?
- formatDereference(*Result.SourceManager, *Arg) :
- getText(*Result.SourceManager, *Arg);
- if (ArgText.empty()) return;
-
- Replace->insert(Replacement(*Result.SourceManager, Call, ArgText));
- }
-
- private:
- tooling::Replacements *Replace;
-};
-} // end namespace
-
-const char *StringConstructor =
- "::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
- "::basic_string";
-
-const char *StringCStrMethod =
- "::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
- "::c_str";
-
-cl::opt<std::string> BuildPath(
- cl::Positional,
- cl::desc("<build-path>"));
-
-cl::list<std::string> SourcePaths(
- cl::Positional,
- cl::desc("<source0> [... <sourceN>]"),
- cl::OneOrMore);
-
-int main(int argc, const char **argv) {
- llvm::sys::PrintStackTraceOnErrorSignal();
- std::unique_ptr<CompilationDatabase> Compilations(
- tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv));
- cl::ParseCommandLineOptions(argc, argv);
- if (!Compilations) {
- std::string ErrorMessage;
- Compilations =
- CompilationDatabase::loadFromDirectory(BuildPath, ErrorMessage);
- if (!Compilations)
- llvm::report_fatal_error(ErrorMessage);
- }
- tooling::RefactoringTool Tool(*Compilations, SourcePaths);
- ast_matchers::MatchFinder Finder;
- FixCStrCall Callback(&Tool.getReplacements());
- Finder.addMatcher(
- constructExpr(
- hasDeclaration(methodDecl(hasName(StringConstructor))),
- argumentCountIs(2),
- // The first argument must have the form x.c_str() or p->c_str()
- // where the method is string::c_str(). We can use the copy
- // constructor of string instead (or the compiler might share
- // the string object).
- hasArgument(
- 0,
- id("call", memberCallExpr(
- callee(id("member", memberExpr())),
- callee(methodDecl(hasName(StringCStrMethod))),
- on(id("arg", expr()))))),
- // The second argument is the alloc object which must not be
- // present explicitly.
- hasArgument(
- 1,
- defaultArgExpr())),
- &Callback);
- Finder.addMatcher(
- constructExpr(
- // Implicit constructors of these classes are overloaded
- // wrt. string types and they internally make a StringRef
- // referring to the argument. Passing a string directly to
- // them is preferred to passing a char pointer.
- hasDeclaration(methodDecl(anyOf(
- hasName("::llvm::StringRef::StringRef"),
- hasName("::llvm::Twine::Twine")))),
- argumentCountIs(1),
- // The only argument must have the form x.c_str() or p->c_str()
- // where the method is string::c_str(). StringRef also has
- // a constructor from string which is more efficient (avoids
- // strlen), so we can construct StringRef from the string
- // directly.
- hasArgument(
- 0,
- id("call", memberCallExpr(
- callee(id("member", memberExpr())),
- callee(methodDecl(hasName(StringCStrMethod))),
- on(id("arg", expr())))))),
- &Callback);
- return Tool.runAndSave(newFrontendActionFactory(&Finder).get());
-}
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -42,7 +42,6 @@
clang-tidy
modularize
pp-trace
- remove-cstr-calls
# Unit tests
ExtraToolsUnitTests
Index: test/clang-tidy/readability-remove-cstr-call.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/readability-remove-cstr-call.cpp
@@ -0,0 +1,39 @@
+// RUN: $(dirname %s)/check_clang_tidy.sh %s readability-remove-cstr-call %t
+// REQUIRES: shell
+
+namespace std {
+template<typename T> class allocator {};
+template<typename T> class char_traits {};
+template<typename C, typename T, typename A> struct basic_string {
+ basic_string();
+ basic_string(const C *p, const A& a = A());
+ const C *c_str() const;
+};
+typedef basic_string<char, std::char_traits<char>, std::allocator<char> > string;
+}
+namespace llvm {
+struct StringRef {
+ StringRef(const char *p);
+ StringRef(const std::string &);
+};
+}
+
+void f1(const std::string &s) {
+ f1(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to `c_str()` [readability-remove-cstr-call]
+ // CHECK-FIXES: {{^ *}}f1(s);{{$}}
+}
+void f2(const llvm::StringRef r) {
+ std::string s;
+ f2(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to `c_str()` [readability-remove-cstr-call]
+ // CHECK-FIXES: {{^ *}}std::string s;{{$}}
+ // CHECK-FIXES-NEXT: {{^ *}}f2(s);{{$}}
+}
+void f3(const llvm::StringRef &r) {
+ std::string s;
+ f3(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to `c_str()` [readability-remove-cstr-call]
+ // CHECK-FIXES: {{^ *}}std::string s;{{$}}
+ // CHECK-FIXES-NEXT: {{^ *}}f3(s);{{$}}
+}
Index: test/remove-cstr-calls/basic.cpp
===================================================================
--- test/remove-cstr-calls/basic.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
-// RUN: remove-cstr-calls . %t.cpp --
-// RUN: FileCheck -input-file=%t.cpp %s
-// REQUIRES: shell
-
-namespace std {
-template<typename T> class allocator {};
-template<typename T> class char_traits {};
-template<typename C, typename T, typename A> struct basic_string {
- basic_string();
- basic_string(const C *p, const A& a = A());
- const C *c_str() const;
-};
-typedef basic_string<char, std::char_traits<char>, std::allocator<char> > string;
-}
-namespace llvm {
-struct StringRef {
- StringRef(const char *p);
- StringRef(const std::string &);
-};
-}
-
-void f1(const std::string &s) {
- f1(s.c_str());
- // CHECK: void f1
- // CHECK-NEXT: f1(s)
-}
-void f2(const llvm::StringRef r) {
- std::string s;
- f2(s.c_str());
- // CHECK: std::string s;
- // CHECK-NEXT: f2(s)
-}
-void f3(const llvm::StringRef &r) {
- std::string s;
- f3(s.c_str());
- // CHECK: std::string s;
- // CHECK: f3(s)
-}
_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic