diff --git a/clang-tools-extra/clangd/SourceCode.h b/clang-tools-extra/clangd/SourceCode.h --- a/clang-tools-extra/clangd/SourceCode.h +++ b/clang-tools-extra/clangd/SourceCode.h @@ -201,6 +201,16 @@ std::vector visibleNamespaces(llvm::StringRef Code, const format::FormatStyle &Style); +struct MacroSym { + llvm::StringRef Name; + const MacroInfo *Info; +}; +// Gets the macro at a specified \p Loc. +llvm::Optional locateMacroAt(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts, + Preprocessor &PP); + } // namespace clangd } // namespace clang #endif diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp --- a/clang-tools-extra/clangd/SourceCode.cpp +++ b/clang-tools-extra/clangd/SourceCode.cpp @@ -16,6 +16,7 @@ #include "clang/Basic/TokenKinds.h" #include "clang/Format/Format.h" #include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" #include "llvm/ADT/None.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -653,5 +654,32 @@ return Result; } +llvm::Optional locateMacroAt(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts, + Preprocessor &PP) { + // Also handle possible macro at the searched location. + Token Result; + if (Lexer::getRawToken(SM.getSpellingLoc(Loc), Result, SM, LangOpts, false)) + return None; + if (Result.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(Result); + IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo(); + if (!IdentifierInfo || !IdentifierInfo->hadMacroDefinition()) + return None; + + std::pair DecLoc = SM.getDecomposedExpansionLoc(Loc); + // Get the definition just before the searched location so that a macro + // referenced in a '#undef MACRO' can still be found. + SourceLocation BeforeSearchedLocation = + SM.getMacroArgExpandedLocation(SM.getLocForStartOfFile(DecLoc.first) + .getLocWithOffset(DecLoc.second - 1)); + MacroDefinition MacroDef = + PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation); + if (auto *MI = MacroDef.getMacroInfo()) + return MacroSym{IdentifierInfo->getName(), MI}; + return None; +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -128,14 +128,9 @@ return Merged.CanonicalDeclaration; } -struct MacroDecl { - llvm::StringRef Name; - const MacroInfo *Info; -}; - /// Finds declarations locations that a given source location refers to. class DeclarationAndMacrosFinder : public index::IndexDataConsumer { - std::vector MacroInfos; + std::vector MacroInfos; llvm::DenseSet Decls; const SourceLocation &SearchedLocation; const ASTContext &AST; @@ -158,14 +153,14 @@ return Result; } - std::vector takeMacroInfos() { + std::vector takeMacroInfos() { // Don't keep the same Macro info multiple times. - llvm::sort(MacroInfos, [](const MacroDecl &Left, const MacroDecl &Right) { + llvm::sort(MacroInfos, [](const MacroSym &Left, const MacroSym &Right) { return Left.Info < Right.Info; }); auto Last = std::unique(MacroInfos.begin(), MacroInfos.end(), - [](const MacroDecl &Left, const MacroDecl &Right) { + [](const MacroSym &Left, const MacroSym &Right) { return Left.Info == Right.Info; }); MacroInfos.erase(Last, MacroInfos.end()); @@ -211,38 +206,17 @@ private: void finish() override { - // Also handle possible macro at the searched location. - Token Result; - auto &Mgr = AST.getSourceManager(); - if (!Lexer::getRawToken(Mgr.getSpellingLoc(SearchedLocation), Result, Mgr, - AST.getLangOpts(), false)) { - if (Result.is(tok::raw_identifier)) { - PP.LookUpIdentifierInfo(Result); - } - IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo(); - if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) { - std::pair DecLoc = - Mgr.getDecomposedExpansionLoc(SearchedLocation); - // Get the definition just before the searched location so that a macro - // referenced in a '#undef MACRO' can still be found. - SourceLocation BeforeSearchedLocation = Mgr.getMacroArgExpandedLocation( - Mgr.getLocForStartOfFile(DecLoc.first) - .getLocWithOffset(DecLoc.second - 1)); - MacroDefinition MacroDef = - PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation); - MacroInfo *MacroInf = MacroDef.getMacroInfo(); - if (MacroInf) { - MacroInfos.push_back(MacroDecl{IdentifierInfo->getName(), MacroInf}); - assert(Decls.empty()); - } - } + if (auto MacroSym = locateMacroAt(SearchedLocation, AST.getSourceManager(), + AST.getLangOpts(), PP)) { + MacroInfos.push_back(*MacroSym); + assert(Decls.empty()); } } }; struct IdentifiedSymbol { std::vector Decls; - std::vector Macros; + std::vector Macros; }; IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) { @@ -740,7 +714,7 @@ } /// Generate a \p Hover object given the macro \p MacroDecl. -static HoverInfo getHoverContents(MacroDecl Decl, ParsedAST &AST) { +static HoverInfo getHoverContents(MacroSym Decl, ParsedAST &AST) { HoverInfo HI; SourceManager &SM = AST.getSourceManager(); HI.Name = Decl.Name; diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -136,6 +136,25 @@ return ReasonToReject::UsedOutsideFile; } +llvm::Error makeError(ReasonToReject Reason) { + auto Message = [](ReasonToReject Reason) { + switch (Reason) { + case NoIndexProvided: + return "symbol may be used in other files (no index available)"; + case UsedOutsideFile: + return "the symbol is used outside main file"; + case NonIndexable: + return "symbol may be used in other files (not eligible for indexing)"; + case UnsupportedSymbol: + return "symbol is not a supported kind (e.g. namespace, macro)"; + } + llvm_unreachable("unhandled reason kind"); + }; + return llvm::make_error( + llvm::formatv("Cannot rename symbol: {0}", Message(Reason)), + llvm::inconvertibleErrorCode()); +} + } // namespace llvm::Expected @@ -145,6 +164,10 @@ ASTContext &ASTCtx = AST.getASTContext(); SourceLocation SourceLocationBeg = clangd::getBeginningOfIdentifier( AST, Pos, AST.getSourceManager().getMainFileID()); + // FIXME: this should be moved to rename tooling library? + if (locateMacroAt(SourceLocationBeg, ASTCtx.getSourceManager(), + ASTCtx.getLangOpts(), AST.getPreprocessor())) + return makeError(UnsupportedSymbol); tooling::RefactoringRuleContext Context(AST.getSourceManager()); Context.setASTContext(ASTCtx); auto Rename = clang::tooling::RenameOccurrences::initiate( @@ -155,24 +178,8 @@ const auto *RenameDecl = Rename->getRenameDecl(); assert(RenameDecl && "symbol must be found at this point"); if (auto Reject = - renamableWithinFile(*RenameDecl->getCanonicalDecl(), File, Index)) { - auto Message = [](ReasonToReject Reason) { - switch (Reason) { - case NoIndexProvided: - return "symbol may be used in other files (no index available)"; - case UsedOutsideFile: - return "the symbol is used outside main file"; - case NonIndexable: - return "symbol may be used in other files (not eligible for indexing)"; - case UnsupportedSymbol: - return "symbol is not a supported kind (e.g. namespace)"; - } - llvm_unreachable("unhandled reason kind"); - }; - return llvm::make_error( - llvm::formatv("Cannot rename symbol: {0}", Message(*Reject)), - llvm::inconvertibleErrorCode()); - } + renamableWithinFile(*RenameDecl->getCanonicalDecl(), File, Index)) + return makeError(*Reject); Rename->invoke(ResultCollector, Context); diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -129,6 +129,13 @@ )cpp", "not a supported kind", HeaderFile}, + { + R"cpp( + #define MACRO 1 + int s = MAC^RO; + )cpp", + "not a supported kind", HeaderFile}, + {R"cpp(// foo is declared outside the file. void fo^o() {} )cpp", "used outside main file", !HeaderFile/*cc file*/},