Index: cfe/trunk/include/clang/Basic/AllDiagnostics.h =================================================================== --- cfe/trunk/include/clang/Basic/AllDiagnostics.h +++ cfe/trunk/include/clang/Basic/AllDiagnostics.h @@ -25,6 +25,7 @@ #include "clang/Parse/ParseDiagnostic.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Serialization/SerializationDiagnostic.h" +#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h" namespace clang { template Index: cfe/trunk/include/clang/Basic/CMakeLists.txt =================================================================== --- cfe/trunk/include/clang/Basic/CMakeLists.txt +++ cfe/trunk/include/clang/Basic/CMakeLists.txt @@ -14,6 +14,7 @@ clang_diag_gen(Frontend) clang_diag_gen(Lex) clang_diag_gen(Parse) +clang_diag_gen(Refactoring) clang_diag_gen(Sema) clang_diag_gen(Serialization) clang_tablegen(DiagnosticGroups.inc -gen-clang-diag-groups Index: cfe/trunk/include/clang/Basic/Diagnostic.td =================================================================== --- cfe/trunk/include/clang/Basic/Diagnostic.td +++ cfe/trunk/include/clang/Basic/Diagnostic.td @@ -138,6 +138,7 @@ include "DiagnosticFrontendKinds.td" include "DiagnosticLexKinds.td" include "DiagnosticParseKinds.td" +include "DiagnosticRefactoringKinds.td" include "DiagnosticSemaKinds.td" include "DiagnosticSerializationKinds.td" Index: cfe/trunk/include/clang/Basic/DiagnosticIDs.h =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticIDs.h +++ cfe/trunk/include/clang/Basic/DiagnosticIDs.h @@ -38,7 +38,8 @@ DIAG_SIZE_COMMENT = 100, DIAG_SIZE_CROSSTU = 100, DIAG_SIZE_SEMA = 3500, - DIAG_SIZE_ANALYSIS = 100 + DIAG_SIZE_ANALYSIS = 100, + DIAG_SIZE_REFACTORING = 1000, }; // Start position for diagnostics. enum { @@ -53,7 +54,8 @@ DIAG_START_CROSSTU = DIAG_START_COMMENT + DIAG_SIZE_CROSSTU, DIAG_START_SEMA = DIAG_START_CROSSTU + DIAG_SIZE_COMMENT, DIAG_START_ANALYSIS = DIAG_START_SEMA + DIAG_SIZE_SEMA, - DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + DIAG_SIZE_ANALYSIS + DIAG_START_REFACTORING = DIAG_START_ANALYSIS + DIAG_SIZE_ANALYSIS, + DIAG_UPPER_LIMIT = DIAG_START_REFACTORING + DIAG_SIZE_REFACTORING }; class CustomDiagInfo; Index: cfe/trunk/include/clang/Basic/DiagnosticRefactoringKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticRefactoringKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticRefactoringKinds.td @@ -0,0 +1,25 @@ +//==--- DiagnosticRefactoringKinds.td - refactoring diagnostics -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Refactoring Diagnostics +//===----------------------------------------------------------------------===// + +let Component = "Refactoring" in { + +let CategoryName = "Refactoring Invocation Issue" in { + +def err_refactor_no_selection : Error<"refactoring action can't be initiated " + "without a selection">; +def err_refactor_selection_no_symbol : Error<"there is no symbol at the given " + "location">; + +} + +} // end of Refactoring diagnostics Index: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h =================================================================== --- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h +++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_RULE_REQUIREMENTS_H #include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h" #include "clang/Tooling/Refactoring/RefactoringOption.h" #include "clang/Tooling/Refactoring/RefactoringRuleContext.h" #include "llvm/Support/Error.h" @@ -47,10 +48,7 @@ Expected evaluate(RefactoringRuleContext &Context) const { if (Context.getSelectionRange().isValid()) return Context.getSelectionRange(); - // FIXME: Use a diagnostic. - return llvm::make_error( - "refactoring action can't be initiated without a selection", - llvm::inconvertibleErrorCode()); + return Context.createDiagnosticError(diag::err_refactor_no_selection); } }; Index: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringDiagnostic.h =================================================================== --- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringDiagnostic.h +++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringDiagnostic.h @@ -0,0 +1,30 @@ +//===--- RefactoringDiagnostic.h - ------------------------------*- 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_TOOLING_REFACTORING_REFACTORINGDIAGNOSTIC_H +#define LLVM_CLANG_TOOLING_REFACTORING_REFACTORINGDIAGNOSTIC_H + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/PartialDiagnostic.h" + +namespace clang { +namespace diag { +enum { +#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, CATEGORY) \ + ENUM, +#define REFACTORINGSTART +#include "clang/Basic/DiagnosticRefactoringKinds.inc" +#undef DIAG + NUM_BUILTIN_REFACTORING_DIAGNOSTICS +}; +} // end namespace diag +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTORING_REFACTORINGDIAGNOSTIC_H Index: cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h =================================================================== --- cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h +++ cfe/trunk/include/clang/Tooling/Refactoring/RefactoringRuleContext.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_RULE_CONTEXT_H #define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_RULE_CONTEXT_H +#include "clang/Basic/DiagnosticError.h" #include "clang/Basic/SourceManager.h" namespace clang { @@ -50,6 +51,17 @@ void setASTContext(ASTContext &Context) { AST = &Context; } + /// Creates an llvm::Error value that contains a diagnostic. + /// + /// The errors should not outlive the context. + llvm::Error createDiagnosticError(SourceLocation Loc, unsigned DiagID) { + return DiagnosticError::create(Loc, PartialDiagnostic(DiagID, DiagStorage)); + } + + llvm::Error createDiagnosticError(unsigned DiagID) { + return createDiagnosticError(SourceLocation(), DiagID); + } + private: /// The source manager for the translation unit / file on which a refactoring /// action might operate on. @@ -60,6 +72,8 @@ /// An optional AST for the translation unit on which a refactoring action /// might operate on. ASTContext *AST = nullptr; + /// The allocator for diagnostics. + PartialDiagnostic::StorageAllocator DiagStorage; }; } // end namespace tooling Index: cfe/trunk/include/clang/module.modulemap =================================================================== --- cfe/trunk/include/clang/module.modulemap +++ cfe/trunk/include/clang/module.modulemap @@ -71,6 +71,7 @@ module Parse { header "Parse/ParseDiagnostic.h" export * } module Sema { header "Sema/SemaDiagnostic.h" export * } module Serialization { header "Serialization/SerializationDiagnostic.h" export * } + module Refactoring { header "Tooling/Refactoring/RefactoringDiagnostic.h" export * } } module Clang_Driver { Index: cfe/trunk/lib/Basic/DiagnosticIDs.cpp =================================================================== --- cfe/trunk/lib/Basic/DiagnosticIDs.cpp +++ cfe/trunk/lib/Basic/DiagnosticIDs.cpp @@ -43,7 +43,7 @@ unsigned SFINAE : 2; unsigned WarnNoWerror : 1; unsigned WarnShowInSystemHeader : 1; - unsigned Category : 5; + unsigned Category : 6; uint16_t OptionGroupIndex; @@ -88,6 +88,7 @@ VALIDATE_DIAG_SIZE(COMMENT) VALIDATE_DIAG_SIZE(SEMA) VALIDATE_DIAG_SIZE(ANALYSIS) +VALIDATE_DIAG_SIZE(REFACTORING) #undef VALIDATE_DIAG_SIZE #undef STRINGIFY_NAME @@ -112,6 +113,7 @@ #include "clang/Basic/DiagnosticCrossTUKinds.inc" #include "clang/Basic/DiagnosticSemaKinds.inc" #include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" #undef DIAG }; @@ -150,6 +152,7 @@ CATEGORY(CROSSTU, COMMENT) CATEGORY(SEMA, CROSSTU) CATEGORY(ANALYSIS, SEMA) +CATEGORY(REFACTORING, ANALYSIS) #undef CATEGORY // Avoid out of bounds reads. Index: cfe/trunk/lib/Tooling/Refactoring/Rename/RenamingAction.cpp =================================================================== --- cfe/trunk/lib/Tooling/Refactoring/Rename/RenamingAction.cpp +++ cfe/trunk/lib/Tooling/Refactoring/Rename/RenamingAction.cpp @@ -23,6 +23,7 @@ #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Refactoring/RefactoringAction.h" +#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h" #include "clang/Tooling/Refactoring/RefactoringOptions.h" #include "clang/Tooling/Refactoring/Rename/SymbolName.h" #include "clang/Tooling/Refactoring/Rename/USRFinder.h" @@ -49,11 +50,9 @@ return Selection.takeError(); const NamedDecl *ND = getNamedDeclAt(Context.getASTContext(), Selection->getBegin()); - if (!ND) { - // FIXME: Use a diagnostic. - return llvm::make_error("no symbol selected", - llvm::inconvertibleErrorCode()); - } + if (!ND) + return Context.createDiagnosticError( + Selection->getBegin(), diag::err_refactor_selection_no_symbol); return getCanonicalSymbolDeclaration(ND); } }; Index: cfe/trunk/test/Refactor/LocalRename/NoSymbolSelectedError.cpp =================================================================== --- cfe/trunk/test/Refactor/LocalRename/NoSymbolSelectedError.cpp +++ cfe/trunk/test/Refactor/LocalRename/NoSymbolSelectedError.cpp @@ -0,0 +1,8 @@ +// RUN: not clang-refactor local-rename -selection=%s:4:1 -new-name=Bar %s -- 2>&1 | FileCheck %s +// RUN: clang-refactor local-rename -selection=test:%s -new-name=Bar %s -- 2>&1 | FileCheck --check-prefix=TESTCHECK %s + +class Baz { // CHECK: [[@LINE]]:1: error: there is no symbol at the given location +}; +/*range=*/; +// TESTCHECK: 1 '' results: +// TESTCHECK-NEXT: there is no symbol at the given location Index: cfe/trunk/tools/clang-refactor/ClangRefactor.cpp =================================================================== --- cfe/trunk/tools/clang-refactor/ClangRefactor.cpp +++ cfe/trunk/tools/clang-refactor/ClangRefactor.cpp @@ -15,6 +15,7 @@ #include "TestSupport.h" #include "clang/Frontend/CommandLineSourceLoc.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Rewrite/Core/Rewriter.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Refactoring.h" @@ -65,7 +66,8 @@ /// logic into the refactoring operation. The test-specific consumer /// ensures that the individual results in a particular test group are /// identical. - virtual std::unique_ptr createCustomConsumer() { + virtual std::unique_ptr + createCustomConsumer() { return nullptr; } @@ -85,7 +87,8 @@ void print(raw_ostream &OS) override { TestSelections.dump(OS); } - std::unique_ptr createCustomConsumer() override { + std::unique_ptr + createCustomConsumer() override { return TestSelections.createConsumer(); } @@ -304,10 +307,20 @@ RefactoringActionCommandLineOptions Options; }; -class ClangRefactorConsumer : public RefactoringResultConsumer { +class ClangRefactorConsumer final : public ClangRefactorToolConsumerInterface { public: + ClangRefactorConsumer() {} + void handleError(llvm::Error Err) override { - llvm::errs() << llvm::toString(std::move(Err)) << "\n"; + Optional Diag = DiagnosticError::take(Err); + if (!Diag) { + llvm::errs() << llvm::toString(std::move(Err)) << "\n"; + return; + } + llvm::cantFail(std::move(Err)); // This is a success. + DiagnosticBuilder DB( + getDiags().Report(Diag->first, Diag->second.getDiagID())); + Diag->second.Emit(DB); } void handle(AtomicChanges Changes) override { @@ -468,8 +481,8 @@ return true; } - bool HasFailed = false; ClangRefactorConsumer Consumer; + bool HasFailed = false; if (foreachTranslationUnit(DB, Sources, [&](ASTContext &AST) { RefactoringRuleContext Context(AST.getSourceManager()); Context.setASTContext(AST); @@ -488,21 +501,27 @@ "The action must have at least one selection rule"); }; + std::unique_ptr CustomConsumer; + if (HasSelection) + CustomConsumer = Subcommand.getSelection()->createCustomConsumer(); + ClangRefactorToolConsumerInterface &ActiveConsumer = + CustomConsumer ? *CustomConsumer : Consumer; + ActiveConsumer.beginTU(AST); if (HasSelection) { assert(Subcommand.getSelection() && "Missing selection argument?"); if (opts::Verbose) Subcommand.getSelection()->print(llvm::outs()); - auto CustomConsumer = - Subcommand.getSelection()->createCustomConsumer(); if (Subcommand.getSelection()->forAllRanges( Context.getSources(), [&](SourceRange R) { Context.setSelectionRange(R); - InvokeRule(CustomConsumer ? *CustomConsumer : Consumer); + InvokeRule(ActiveConsumer); })) HasFailed = true; + ActiveConsumer.endTU(); return; } // FIXME (Alex L): Implement non-selection based invocation path. + ActiveConsumer.endTU(); })) return true; return HasFailed || applySourceChanges(Consumer.getSourceChanges()); Index: cfe/trunk/tools/clang-refactor/TestSupport.h =================================================================== --- cfe/trunk/tools/clang-refactor/TestSupport.h +++ cfe/trunk/tools/clang-refactor/TestSupport.h @@ -16,9 +16,9 @@ #ifndef LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H #define LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H +#include "ToolRefactoringResultConsumer.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" -#include "clang/Tooling/Refactoring/RefactoringResultConsumer.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" @@ -65,7 +65,7 @@ bool foreachRange(const SourceManager &SM, llvm::function_ref Callback) const; - std::unique_ptr createConsumer() const; + std::unique_ptr createConsumer() const; void dump(llvm::raw_ostream &OS) const; }; Index: cfe/trunk/tools/clang-refactor/TestSupport.cpp =================================================================== --- cfe/trunk/tools/clang-refactor/TestSupport.cpp +++ cfe/trunk/tools/clang-refactor/TestSupport.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "TestSupport.h" +#include "clang/Basic/DiagnosticError.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/STLExtras.h" @@ -106,7 +107,7 @@ } class TestRefactoringResultConsumer final - : public tooling::RefactoringResultConsumer { + : public ClangRefactorToolConsumerInterface { public: TestRefactoringResultConsumer(const TestSelectionRangesInFile &TestRanges) : TestRanges(TestRanges) { @@ -182,10 +183,15 @@ std::string ErrorMessage; bool HasResult = !!Result; if (!HasResult) { - // FIXME: Handle diagnostic error as well. - handleAllErrors(Result.takeError(), [&](StringError &Err) { - ErrorMessage = Err.getMessage(); - }); + handleAllErrors( + Result.takeError(), + [&](StringError &Err) { ErrorMessage = Err.getMessage(); }, + [&](DiagnosticError &Err) { + const PartialDiagnosticAt &Diag = Err.getDiagnostic(); + llvm::SmallString<100> DiagText; + Diag.second.EmitToString(getDiags(), DiagText); + ErrorMessage = DiagText.str().str(); + }); } if (!CanonicalResult && !CanonicalErrorMessage) { if (HasResult) @@ -248,7 +254,7 @@ return Failed; } -std::unique_ptr +std::unique_ptr TestSelectionRangesInFile::createConsumer() const { return llvm::make_unique(*this); } Index: cfe/trunk/tools/clang-refactor/ToolRefactoringResultConsumer.h =================================================================== --- cfe/trunk/tools/clang-refactor/ToolRefactoringResultConsumer.h +++ cfe/trunk/tools/clang-refactor/ToolRefactoringResultConsumer.h @@ -0,0 +1,48 @@ +//===--- ToolRefactoringResultConsumer.h - ----------------------*- 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_CLANG_REFACTOR_TOOL_REFACTORING_RESULT_CONSUMER_H +#define LLVM_CLANG_TOOLS_CLANG_REFACTOR_TOOL_REFACTORING_RESULT_CONSUMER_H + +#include "clang/AST/ASTContext.h" +#include "clang/Tooling/Refactoring/RefactoringResultConsumer.h" + +namespace clang { +namespace refactor { + +/// An interface that subclasses the \c RefactoringResultConsumer interface +/// that stores the reference to the TU-specific diagnostics engine. +class ClangRefactorToolConsumerInterface + : public tooling::RefactoringResultConsumer { +public: + /// Called when a TU is entered. + void beginTU(ASTContext &Context) { + assert(!Diags && "Diags has been set"); + Diags = &Context.getDiagnostics(); + } + + /// Called when the tool is done with a TU. + void endTU() { + assert(Diags && "Diags unset"); + Diags = nullptr; + } + + DiagnosticsEngine &getDiags() const { + assert(Diags && "no diags"); + return *Diags; + } + +private: + DiagnosticsEngine *Diags = nullptr; +}; + +} // end namespace refactor +} // end namespace clang + +#endif // LLVM_CLANG_TOOLS_CLANG_REFACTOR_TOOL_REFACTORING_RESULT_CONSUMER_H Index: cfe/trunk/tools/diagtool/DiagnosticNames.cpp =================================================================== --- cfe/trunk/tools/diagtool/DiagnosticNames.cpp +++ cfe/trunk/tools/diagtool/DiagnosticNames.cpp @@ -42,6 +42,7 @@ #include "clang/Basic/DiagnosticCommentKinds.inc" #include "clang/Basic/DiagnosticSemaKinds.inc" #include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" #undef DIAG }; Index: cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp =================================================================== --- cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp +++ cfe/trunk/unittests/Tooling/RefactoringActionRulesTest.cpp @@ -11,6 +11,7 @@ #include "RewriterTestContext.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Refactoring/RefactoringActionRules.h" +#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h" #include "clang/Tooling/Refactoring/Rename/SymbolName.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/Errc.h" @@ -128,12 +129,12 @@ createReplacements(Rule, RefContext); ASSERT_TRUE(!ErrorOrResult); - std::string Message; - llvm::handleAllErrors( - ErrorOrResult.takeError(), - [&](llvm::StringError &Error) { Message = Error.getMessage(); }); - EXPECT_EQ(Message, - "refactoring action can't be initiated without a selection"); + unsigned DiagID; + llvm::handleAllErrors(ErrorOrResult.takeError(), + [&](DiagnosticError &Error) { + DiagID = Error.getDiagnostic().second.getDiagID(); + }); + EXPECT_EQ(DiagID, diag::err_refactor_no_selection); } }