diff --git a/clang-tools-extra/clangd/Selection.h b/clang-tools-extra/clangd/Selection.h --- a/clang-tools-extra/clangd/Selection.h +++ b/clang-tools-extra/clangd/Selection.h @@ -35,6 +35,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SELECTION_H #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/PrettyPrinter.h" +#include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/SmallVector.h" namespace clang { @@ -56,8 +57,9 @@ // // SelectionTree tries to behave sensibly in the presence of macros, but does // not model any preprocessor concepts: the output is a subset of the AST. -// Currently comments, directives etc are treated as part of the lexically -// containing AST node, (though we may want to change this in future). +// +// Comments, directives and whitespace are completely ignored. +// Semicolons are also ignored, as the AST generally does not model them well. // // The SelectionTree owns the Node structures, but the ASTNode attributes // point back into the AST it was constructed with. @@ -66,11 +68,13 @@ // Creates a selection tree at the given byte offset in the main file. // This is approximately equivalent to a range of one character. // (Usually, the character to the right of Offset, sometimes to the left). - SelectionTree(ASTContext &AST, unsigned Offset); + SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens, + unsigned Offset); // Creates a selection tree for the given range in the main file. // The range includes bytes [Start, End). // If Start == End, uses the same heuristics as SelectionTree(AST, Start). - SelectionTree(ASTContext &AST, unsigned Start, unsigned End); + SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens, + unsigned Start, unsigned End); // Describes to what extent an AST node is covered by the selection. enum Selection { diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -17,6 +17,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TokenKinds.h" +#include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" #include @@ -28,39 +29,83 @@ using Node = SelectionTree::Node; using ast_type_traits::DynTypedNode; -// Stores a collection of (possibly-overlapping) integer ranges. -// When new ranges are added, hit-tests them against existing ones. -class RangeSet { +// Identifies which tokens are selected, and evaluates claims of source ranges +// by AST nodes. Tokens may be claimed only once. +class SelectedTokens { public: - // Returns true if any new offsets are covered. - // This is naive (linear in number of successful add() calls), but ok for now. - bool add(unsigned Begin, unsigned End) { - assert(std::is_sorted(Ranges.begin(), Ranges.end())); - assert(Begin < End); - - if (covered(Begin, End)) - return false; - auto Pair = std::make_pair(Begin, End); - Ranges.insert(llvm::upper_bound(Ranges, Pair), Pair); - return true; + SelectedTokens(llvm::ArrayRef Spelled, const SourceManager &SM, + unsigned SelBegin, unsigned SelEnd) + : SelBegin(SelBegin), SelEnd(SelEnd) { + Tokens.reserve(Spelled.size()); + for (const auto& Tok : Spelled) { + // As well as comments, don't count semicolons as real tokens. + // They're not properly claimed as expr-statement is missing from the AST. + if (Tok.kind() == tok::comment || Tok.kind() == tok::semi) + continue; + + Tokens.emplace_back(); + TokInfo &S = Tokens.back(); + S.StartOffset = SM.getFileOffset(Tok.location()); + S.EndOffset = S.StartOffset + Tok.length(); + if (S.StartOffset >= SelBegin && S.EndOffset <= SelEnd) + S.Selected = SelectionTree::Complete; + else if (S.EndOffset > SelBegin && S.StartOffset < SelEnd) + S.Selected = SelectionTree::Partial; + else + S.Selected = SelectionTree::Unselected; + S.Claimed = false; + } } -private: - bool covered(unsigned Begin, unsigned End) { - assert(Begin < End); - for (const auto &R : Ranges) { - if (Begin < R.first) - return false; // The prefix [Begin, R.first) is not covered. - if (Begin < R.second) { - Begin = R.second; // Prefix is covered, truncate the range. - if (Begin >= End) - return true; + SelectionTree::Selection claim(unsigned Begin, unsigned End) { + assert(Begin <= End); + + // Fast-path for missing the selection entirely. + if (Begin >= SelEnd || End <= SelBegin) + return SelectionTree::Unselected; + + // We will consider the range selected if it hit any selected and previously + // unclaimed token. + bool ClaimedAnyToken = false; + // The selection is partial if: + // - any claimed token is partially selected + // - any token in the range is unselected + bool PartialSelection = false; + + // Find the first token that (maybe) overlaps the claimed range. + auto Start = llvm::partition_point(Tokens, [&](const TokInfo &Tok) { + return Tok.EndOffset <= Begin; + }); + // Iterate over every token that overlaps the range. + // Claim selected tokens, and update the two result flags. + for (auto It = Start; It != Tokens.end() && It->StartOffset < End; ++It) { + if (It->Selected) { + if (!It->Claimed) { + It->Claimed = true; + ClaimedAnyToken = true; + PartialSelection |= (It->Selected == SelectionTree::Partial); + } + } else { + PartialSelection = true; } } - return false; + if (!ClaimedAnyToken) + return SelectionTree::Unselected; + return PartialSelection ? SelectionTree::Partial : SelectionTree::Complete; } - std::vector> Ranges; // Always sorted. +private: + struct TokInfo { + unsigned StartOffset; + unsigned EndOffset; + SelectionTree::Selection Selected; + bool Claimed; + bool operator<(const TokInfo &Other) const { + return StartOffset < Other.StartOffset; + } + }; + std::vector Tokens; + unsigned SelBegin, SelEnd; }; // Show the type of a node for debugging. @@ -99,14 +144,16 @@ public: // Runs the visitor to gather selected nodes and their ancestors. // If there is any selection, the root (TUDecl) is the first node. - static std::deque collect(ASTContext &AST, const PrintingPolicy &PP, - unsigned Begin, unsigned End, FileID File) { - SelectionVisitor V(AST, PP, Begin, End, File); + static std::deque collect(ASTContext &AST, + const syntax::TokenBuffer &Tokens, + const PrintingPolicy &PP, unsigned Begin, + unsigned End, FileID File) { + SelectionVisitor V(AST, Tokens, PP, Begin, End, File); V.TraverseAST(AST); assert(V.Stack.size() == 1 && "Unpaired push/pop?"); assert(V.Stack.top() == &V.Nodes.front()); - // We selected TUDecl if characters were unclaimed (or the file is empty). - if (V.Nodes.size() == 1 || V.Claimed.add(Begin, End)) { + // We selected TUDecl if tokens were unclaimed (or the file is empty). + if (V.Nodes.size() == 1 || V.Claimed.claim(Begin, End)) { StringRef FileContent = AST.getSourceManager().getBufferData(File); // Don't require the trailing newlines to be selected. bool SelectedAll = Begin == 0 && End >= FileContent.rtrim().size(); @@ -176,15 +223,18 @@ private: using Base = RecursiveASTVisitor; - SelectionVisitor(ASTContext &AST, const PrintingPolicy &PP, unsigned SelBegin, - unsigned SelEnd, FileID SelFile) + SelectionVisitor(ASTContext &AST, const syntax::TokenBuffer &Tokens, + const PrintingPolicy &PP, unsigned SelBegin, unsigned SelEnd, + FileID SelFile) : SM(AST.getSourceManager()), LangOpts(AST.getLangOpts()), #ifndef NDEBUG PrintPolicy(PP), #endif - SelBegin(SelBegin), SelEnd(SelEnd), SelFile(SelFile), + Claimed(Tokens.spelledTokens(SelFile), SM, SelBegin, SelEnd), + SelFile(SelFile), SelBeginTokenStart(SM.getFileOffset(Lexer::GetBeginningOfToken( - SM.getComposedLoc(SelFile, SelBegin), SM, LangOpts))) { + SM.getComposedLoc(SelFile, SelBegin), SM, LangOpts))), + SelEnd(SelEnd) { // Ensure we have a node for the TU decl, regardless of traversal scope. Nodes.emplace_back(); Nodes.back().ASTNode = DynTypedNode::create(*AST.getTranslationUnitDecl()); @@ -318,30 +368,16 @@ // Otherwise, nodes in macro expansions can't be selected. if (B.first != SelFile || E.first != SelFile) return SelectionTree::Unselected; - // Is there any overlap at all between the selection and range? - if (B.second >= SelEnd || E.second < SelBegin) - return SelectionTree::Unselected; - // We may have hit something. - auto PreciseBounds = std::make_pair(B.second, E.second); - // Trim range using the selection, drop it if empty. - B.second = std::max(B.second, SelBegin); - E.second = std::min(E.second, SelEnd); - if (B.second >= E.second) - return SelectionTree::Unselected; // Attempt to claim the remaining range. If there's nothing to claim, only // children were selected. - if (!Claimed.add(B.second, E.second)) - return SelectionTree::Unselected; - dlog("{1}hit selection: {0}", - SourceRange(SM.getComposedLoc(B.first, B.second), - SM.getComposedLoc(E.first, E.second)) - .printToString(SM), - indent()); - // Some of our own characters are covered, this is a true hit. - // Determine whether the node was completely covered. - return (PreciseBounds.first >= SelBegin && PreciseBounds.second <= SelEnd) - ? SelectionTree::Complete - : SelectionTree::Partial; + SelectionTree::Selection Result = Claimed.claim(B.second, E.second); + if (Result) + dlog("{1}hit selection: {0}", + SourceRange(SM.getComposedLoc(B.first, B.second), + SM.getComposedLoc(E.first, E.second)) + .printToString(SM), + indent()); + return Result; } std::string indent(int Offset = 0) { @@ -357,11 +393,8 @@ const PrintingPolicy &PrintPolicy; #endif std::stack Stack; - RangeSet Claimed; + SelectedTokens Claimed; std::deque Nodes; // Stable pointers as we add more nodes. - // Half-open selection range. - unsigned SelBegin; - unsigned SelEnd; FileID SelFile; // If the selection start slices a token in half, the beginning of that token. // This is useful for checking whether the end of a token range overlaps @@ -369,6 +402,7 @@ // range.end + measureToken(range.end) < SelBegin (assuming range.end points // to a token), and it saves a lex every time. unsigned SelBeginTokenStart; + unsigned SelEnd; }; } // namespace @@ -414,7 +448,8 @@ return {Offset, Offset + 1}; } -SelectionTree::SelectionTree(ASTContext &AST, unsigned Begin, unsigned End) +SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens, + unsigned Begin, unsigned End) : PrintPolicy(AST.getLangOpts()) { // No fundamental reason the selection needs to be in the main file, // but that's all clangd has needed so far. @@ -428,13 +463,14 @@ dlog("Computing selection for {0}", SourceRange(SM.getComposedLoc(FID, Begin), SM.getComposedLoc(FID, End)) .printToString(SM)); - Nodes = SelectionVisitor::collect(AST, PrintPolicy, Begin, End, FID); + Nodes = SelectionVisitor::collect(AST, Tokens, PrintPolicy, Begin, End, FID); Root = Nodes.empty() ? nullptr : &Nodes.front(); dlog("Built selection tree\n{0}", *this); } -SelectionTree::SelectionTree(ASTContext &AST, unsigned Offset) - : SelectionTree(AST, Offset, Offset) {} +SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens, + unsigned Offset) + : SelectionTree(AST, Tokens, Offset, Offset) {} const Node *SelectionTree::commonAncestor() const { const Node *Ancestor = Root; diff --git a/clang-tools-extra/clangd/refactor/Tweak.cpp b/clang-tools-extra/clangd/refactor/Tweak.cpp --- a/clang-tools-extra/clangd/refactor/Tweak.cpp +++ b/clang-tools-extra/clangd/refactor/Tweak.cpp @@ -41,7 +41,7 @@ Tweak::Selection::Selection(ParsedAST &AST, unsigned RangeBegin, unsigned RangeEnd) : AST(AST), SelectionBegin(RangeBegin), SelectionEnd(RangeEnd), - ASTSelection(AST.getASTContext(), RangeBegin, RangeEnd) { + ASTSelection(AST.getASTContext(), AST.getTokens(), RangeBegin, RangeEnd) { auto &SM = AST.getSourceManager(); Code = SM.getBufferData(SM.getMainFileID()); Cursor = SM.getComposedLoc(SM.getMainFileID(), RangeBegin); diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -23,16 +23,16 @@ Annotations Test(MarkedCode); switch (Test.points().size()) { case 1: // Point selection. - return SelectionTree(AST.getASTContext(), + return SelectionTree(AST.getASTContext(), AST.getTokens(), cantFail(positionToOffset(Test.code(), Test.point()))); case 2: // Range selection. return SelectionTree( - AST.getASTContext(), + AST.getASTContext(), AST.getTokens(), cantFail(positionToOffset(Test.code(), Test.points()[0])), cantFail(positionToOffset(Test.code(), Test.points()[1]))); default: ADD_FAILURE() << "Expected 1-2 points for selection.\n" << MarkedCode; - return SelectionTree(AST.getASTContext(), 0u, 0u); + return SelectionTree(AST.getASTContext(), AST.getTokens(), 0u, 0u); } } @@ -219,6 +219,9 @@ {"void foo() { [[foo^]] (); }", "DeclRefExpr"}, {"int bar; void foo() [[{ foo (); }]]^", "CompoundStmt"}, + // Ignores whitespace, comments, and semicolons in the selection. + {"void foo() { [[foo^()]]; /*comment*/^}", "CallExpr"}, + // Tricky case: FunctionTypeLoc in FunctionDecl has a hole in it. {"[[^void]] foo();", "BuiltinTypeLoc"}, {"[[void foo^()]];", "FunctionProtoTypeLoc"}, diff --git a/clang/include/clang/Driver/Types.h b/clang/include/clang/Driver/Types.h --- a/clang/include/clang/Driver/Types.h +++ b/clang/include/clang/Driver/Types.h @@ -20,7 +20,7 @@ namespace types { enum ID { TY_INVALID, -#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, ...) TY_##ID, +#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, FLAGS, ...) TY_##ID, #include "clang/Driver/Types.def" #undef TYPE TY_LAST diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def --- a/clang/include/clang/Driver/Types.def +++ b/clang/include/clang/Driver/Types.def @@ -29,73 +29,76 @@ // The fourth value is the suffix to use when creating temporary files // of this type, or null if unspecified. -// The final value is a variadic list of phases for each type. Eventually the +// The fifth value is a string containing option flags. Valid values: +// u - The type can be user specified (with -x). + +// The sixth value is a variadic list of phases for each type. Eventually the // options flag string will be replaced with this variadic list. // Most of the options in Flags have been removed in favor of subsuming their // meaning from the phases list. // C family source language (with and without preprocessing). -TYPE("cpp-output", PP_C, INVALID, "i", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("c", C, PP_C, "c", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("cl", CL, PP_C, "cl", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("cuda-cpp-output", PP_CUDA, INVALID, "cui", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("cuda", CUDA, PP_CUDA, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("cuda", CUDA_DEVICE, PP_CUDA, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("hip-cpp-output", PP_HIP, INVALID, "cui", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("hip", HIP, PP_HIP, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("hip", HIP_DEVICE, PP_HIP, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("objective-c-cpp-output", PP_ObjC, INVALID, "mi", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("objc-cpp-output", PP_ObjC_Alias, INVALID, "mi", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("objective-c", ObjC, PP_ObjC, "m", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("c++-cpp-output", PP_CXX, INVALID, "ii", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("c++", CXX, PP_CXX, "cpp", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("objective-c++-cpp-output", PP_ObjCXX, INVALID, "mii", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("objc++-cpp-output", PP_ObjCXX_Alias, INVALID, "mii", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("objective-c++", ObjCXX, PP_ObjCXX, "mm", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("renderscript", RenderScript, PP_C, "rs", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cpp-output", PP_C, INVALID, "i", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c", C, PP_C, "c", "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cl", CL, PP_C, "cl", "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cuda-cpp-output", PP_CUDA, INVALID, "cui", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cuda", CUDA, PP_CUDA, "cu", "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cuda", CUDA_DEVICE, PP_CUDA, "cu", "" , phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip-cpp-output", PP_HIP, INVALID, "cui", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip", HIP, PP_HIP, "cu", "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip", HIP_DEVICE, PP_HIP, "cu", "" , phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objective-c-cpp-output", PP_ObjC, INVALID, "mi", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objc-cpp-output", PP_ObjC_Alias, INVALID, "mi", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objective-c", ObjC, PP_ObjC, "m", "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c++-cpp-output", PP_CXX, INVALID, "ii", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c++", CXX, PP_CXX, "cpp", "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objective-c++-cpp-output", PP_ObjCXX, INVALID, "mii", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objc++-cpp-output", PP_ObjCXX_Alias, INVALID, "mii", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objective-c++", ObjCXX, PP_ObjCXX, "mm", "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("renderscript", RenderScript, PP_C, "rs", "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) // C family input files to precompile. -TYPE("c-header-cpp-output", PP_CHeader, INVALID, "i", phases::Precompile) -TYPE("c-header", CHeader, PP_CHeader, "h", phases::Preprocess, phases::Precompile) -TYPE("cl-header", CLHeader, PP_CHeader, "h", phases::Preprocess, phases::Precompile) -TYPE("objective-c-header-cpp-output", PP_ObjCHeader, INVALID, "mi", phases::Precompile) -TYPE("objective-c-header", ObjCHeader, PP_ObjCHeader, "h", phases::Preprocess, phases::Precompile) -TYPE("c++-header-cpp-output", PP_CXXHeader, INVALID, "ii", phases::Precompile) -TYPE("c++-header", CXXHeader, PP_CXXHeader, "hh", phases::Preprocess, phases::Precompile) -TYPE("objective-c++-header-cpp-output", PP_ObjCXXHeader, INVALID, "mii", phases::Precompile) -TYPE("objective-c++-header", ObjCXXHeader, PP_ObjCXXHeader, "h", phases::Preprocess, phases::Precompile) -TYPE("c++-module", CXXModule, PP_CXXModule, "cppm", phases::Preprocess, phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("c++-module-cpp-output", PP_CXXModule, INVALID, "iim", phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c-header-cpp-output", PP_CHeader, INVALID, "i", "", phases::Precompile) +TYPE("c-header", CHeader, PP_CHeader, "h", "u", phases::Preprocess, phases::Precompile) +TYPE("cl-header", CLHeader, PP_CHeader, "h", "u", phases::Preprocess, phases::Precompile) +TYPE("objective-c-header-cpp-output", PP_ObjCHeader, INVALID, "mi", "", phases::Precompile) +TYPE("objective-c-header", ObjCHeader, PP_ObjCHeader, "h", "u", phases::Preprocess, phases::Precompile) +TYPE("c++-header-cpp-output", PP_CXXHeader, INVALID, "ii", "", phases::Precompile) +TYPE("c++-header", CXXHeader, PP_CXXHeader, "hh", "u", phases::Preprocess, phases::Precompile) +TYPE("objective-c++-header-cpp-output", PP_ObjCXXHeader, INVALID, "mii", "", phases::Precompile) +TYPE("objective-c++-header", ObjCXXHeader, PP_ObjCXXHeader, "h", "u", phases::Preprocess, phases::Precompile) +TYPE("c++-module", CXXModule, PP_CXXModule, "cppm", "u", phases::Preprocess, phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c++-module-cpp-output", PP_CXXModule, INVALID, "iim", "", phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) // Other languages. -TYPE("ada", Ada, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("assembler", PP_Asm, INVALID, "s", phases::Assemble, phases::Link) -TYPE("assembler-with-cpp", Asm, PP_Asm, "S", phases::Preprocess, phases::Assemble, phases::Link) -TYPE("f95", PP_Fortran, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("f95-cpp-input", Fortran, PP_Fortran, nullptr, phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("java", Java, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("ada", Ada, INVALID, nullptr, "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("assembler", PP_Asm, INVALID, "s", "u", phases::Assemble, phases::Link) +TYPE("assembler-with-cpp", Asm, PP_Asm, "S", "u", phases::Preprocess, phases::Assemble, phases::Link) +TYPE("f95", PP_Fortran, INVALID, nullptr, "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("f95-cpp-input", Fortran, PP_Fortran, nullptr, "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("java", Java, INVALID, nullptr, "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) // LLVM IR/LTO types. We define separate types for IR and LTO because LTO // outputs should use the standard suffixes. -TYPE("ir", LLVM_IR, INVALID, "ll", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("ir", LLVM_BC, INVALID, "bc", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("lto-ir", LTO_IR, INVALID, "s", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("lto-bc", LTO_BC, INVALID, "o", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("ir", LLVM_IR, INVALID, "ll", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("ir", LLVM_BC, INVALID, "bc", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("lto-ir", LTO_IR, INVALID, "s", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("lto-bc", LTO_BC, INVALID, "o", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) // Misc. -TYPE("ast", AST, INVALID, "ast", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("ifs", IFS, INVALID, "ifs", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("pcm", ModuleFile, INVALID, "pcm", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("plist", Plist, INVALID, "plist", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("rewritten-objc", RewrittenObjC,INVALID, "cpp", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("rewritten-legacy-objc", RewrittenLegacyObjC,INVALID, "cpp", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("remap", Remap, INVALID, "remap", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("precompiled-header", PCH, INVALID, "gch", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("object", Object, INVALID, "o", phases::Link) -TYPE("treelang", Treelang, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("image", Image, INVALID, "out", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("dSYM", dSYM, INVALID, "dSYM", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("dependencies", Dependencies, INVALID, "d", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("cuda-fatbin", CUDA_FATBIN, INVALID, "fatbin", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("hip-fatbin", HIP_FATBIN, INVALID, "hipfb", phases::Compile, phases::Backend, phases::Assemble, phases::Link) -TYPE("none", Nothing, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("ast", AST, INVALID, "ast", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("ifs", IFS, INVALID, "ifs", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("pcm", ModuleFile, INVALID, "pcm", "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("plist", Plist, INVALID, "plist", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("rewritten-objc", RewrittenObjC,INVALID, "cpp", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("rewritten-legacy-objc", RewrittenLegacyObjC,INVALID, "cpp", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("remap", Remap, INVALID, "remap", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("precompiled-header", PCH, INVALID, "gch", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("object", Object, INVALID, "o", "", phases::Link) +TYPE("treelang", Treelang, INVALID, nullptr, "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("image", Image, INVALID, "out", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("dSYM", dSYM, INVALID, "dSYM", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("dependencies", Dependencies, INVALID, "d", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cuda-fatbin", CUDA_FATBIN, INVALID, "fatbin","", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip-fatbin", HIP_FATBIN, INVALID, "hipfb", "", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("none", Nothing, INVALID, nullptr, "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link) diff --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp --- a/clang/lib/Driver/Types.cpp +++ b/clang/lib/Driver/Types.cpp @@ -18,14 +18,15 @@ struct TypeInfo { const char *Name; + const char *Flags; const char *TempSuffix; ID PreprocessedType; const llvm::SmallVector Phases; }; static const TypeInfo TypeInfos[] = { -#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, ...) \ - { NAME, TEMP_SUFFIX, TY_##PP_TYPE, { __VA_ARGS__ }, }, +#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, FLAGS, ...) \ + { NAME, FLAGS, TEMP_SUFFIX, TY_##PP_TYPE, { __VA_ARGS__ }, }, #include "clang/Driver/Types.def" #undef TYPE }; @@ -89,15 +90,7 @@ } bool types::canTypeBeUserSpecified(ID Id) { - static const clang::driver::types::ID kStaticLangageTypes[] = { - TY_CUDA_DEVICE, TY_HIP_DEVICE, TY_PP_CHeader, - TY_PP_ObjCHeader, TY_PP_CXXHeader, TY_ObjCXXHeader, - TY_PP_CXXModule, TY_LTO_IR, TY_LTO_BC, - TY_Plist, TY_RewrittenObjC, TY_RewrittenLegacyObjC, - TY_Remap, TY_PCH, TY_Object, - TY_Image, TY_dSYM, TY_Dependencies, - TY_CUDA_FATBIN, TY_HIP_FATBIN}; - return !llvm::is_contained(kStaticLangageTypes, Id); + return strchr(getInfo(Id).Flags, 'u'); } bool types::appendSuffixForType(ID Id) {