1*f4a2713aSLionel Sambuc //===--- FileMatchTrie.cpp - ----------------------------------------------===// 2*f4a2713aSLionel Sambuc // 3*f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure 4*f4a2713aSLionel Sambuc // 5*f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source 6*f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details. 7*f4a2713aSLionel Sambuc // 8*f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===// 9*f4a2713aSLionel Sambuc // 10*f4a2713aSLionel Sambuc // This file contains the implementation of a FileMatchTrie. 11*f4a2713aSLionel Sambuc // 12*f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===// 13*f4a2713aSLionel Sambuc 14*f4a2713aSLionel Sambuc #include "clang/Tooling/FileMatchTrie.h" 15*f4a2713aSLionel Sambuc #include "llvm/ADT/StringMap.h" 16*f4a2713aSLionel Sambuc #include "llvm/Support/FileSystem.h" 17*f4a2713aSLionel Sambuc #include "llvm/Support/Path.h" 18*f4a2713aSLionel Sambuc #include "llvm/Support/raw_ostream.h" 19*f4a2713aSLionel Sambuc #include <sstream> 20*f4a2713aSLionel Sambuc 21*f4a2713aSLionel Sambuc namespace clang { 22*f4a2713aSLionel Sambuc namespace tooling { 23*f4a2713aSLionel Sambuc 24*f4a2713aSLionel Sambuc /// \brief Default \c PathComparator using \c llvm::sys::fs::equivalent(). 25*f4a2713aSLionel Sambuc struct DefaultPathComparator : public PathComparator { 26*f4a2713aSLionel Sambuc virtual ~DefaultPathComparator() {} 27*f4a2713aSLionel Sambuc virtual bool equivalent(StringRef FileA, StringRef FileB) const { 28*f4a2713aSLionel Sambuc return FileA == FileB || llvm::sys::fs::equivalent(FileA, FileB); 29*f4a2713aSLionel Sambuc } 30*f4a2713aSLionel Sambuc }; 31*f4a2713aSLionel Sambuc 32*f4a2713aSLionel Sambuc /// \brief A node of the \c FileMatchTrie. 33*f4a2713aSLionel Sambuc /// 34*f4a2713aSLionel Sambuc /// Each node has storage for up to one path and a map mapping a path segment to 35*f4a2713aSLionel Sambuc /// child nodes. The trie starts with an empty root node. 36*f4a2713aSLionel Sambuc class FileMatchTrieNode { 37*f4a2713aSLionel Sambuc public: 38*f4a2713aSLionel Sambuc /// \brief Inserts 'NewPath' into this trie. \c ConsumedLength denotes 39*f4a2713aSLionel Sambuc /// the number of \c NewPath's trailing characters already consumed during 40*f4a2713aSLionel Sambuc /// recursion. 41*f4a2713aSLionel Sambuc /// 42*f4a2713aSLionel Sambuc /// An insert of a path 43*f4a2713aSLionel Sambuc /// 'p'starts at the root node and does the following: 44*f4a2713aSLionel Sambuc /// - If the node is empty, insert 'p' into its storage and abort. 45*f4a2713aSLionel Sambuc /// - If the node has a path 'p2' but no children, take the last path segment 46*f4a2713aSLionel Sambuc /// 's' of 'p2', put a new child into the map at 's' an insert the rest of 47*f4a2713aSLionel Sambuc /// 'p2' there. 48*f4a2713aSLionel Sambuc /// - Insert a new child for the last segment of 'p' and insert the rest of 49*f4a2713aSLionel Sambuc /// 'p' there. 50*f4a2713aSLionel Sambuc /// 51*f4a2713aSLionel Sambuc /// An insert operation is linear in the number of a path's segments. 52*f4a2713aSLionel Sambuc void insert(StringRef NewPath, unsigned ConsumedLength = 0) { 53*f4a2713aSLionel Sambuc // We cannot put relative paths into the FileMatchTrie as then a path can be 54*f4a2713aSLionel Sambuc // a postfix of another path, violating a core assumption of the trie. 55*f4a2713aSLionel Sambuc if (llvm::sys::path::is_relative(NewPath)) 56*f4a2713aSLionel Sambuc return; 57*f4a2713aSLionel Sambuc if (Path.empty()) { 58*f4a2713aSLionel Sambuc // This is an empty leaf. Store NewPath and return. 59*f4a2713aSLionel Sambuc Path = NewPath; 60*f4a2713aSLionel Sambuc return; 61*f4a2713aSLionel Sambuc } 62*f4a2713aSLionel Sambuc if (Children.empty()) { 63*f4a2713aSLionel Sambuc // This is a leaf, ignore duplicate entry if 'Path' equals 'NewPath'. 64*f4a2713aSLionel Sambuc if (NewPath == Path) 65*f4a2713aSLionel Sambuc return; 66*f4a2713aSLionel Sambuc // Make this a node and create a child-leaf with 'Path'. 67*f4a2713aSLionel Sambuc StringRef Element(llvm::sys::path::filename( 68*f4a2713aSLionel Sambuc StringRef(Path).drop_back(ConsumedLength))); 69*f4a2713aSLionel Sambuc Children[Element].Path = Path; 70*f4a2713aSLionel Sambuc } 71*f4a2713aSLionel Sambuc StringRef Element(llvm::sys::path::filename( 72*f4a2713aSLionel Sambuc StringRef(NewPath).drop_back(ConsumedLength))); 73*f4a2713aSLionel Sambuc Children[Element].insert(NewPath, ConsumedLength + Element.size() + 1); 74*f4a2713aSLionel Sambuc } 75*f4a2713aSLionel Sambuc 76*f4a2713aSLionel Sambuc /// \brief Tries to find the node under this \c FileMatchTrieNode that best 77*f4a2713aSLionel Sambuc /// matches 'FileName'. 78*f4a2713aSLionel Sambuc /// 79*f4a2713aSLionel Sambuc /// If multiple paths fit 'FileName' equally well, \c IsAmbiguous is set to 80*f4a2713aSLionel Sambuc /// \c true and an empty string is returned. If no path fits 'FileName', an 81*f4a2713aSLionel Sambuc /// empty string is returned. \c ConsumedLength denotes the number of 82*f4a2713aSLionel Sambuc /// \c Filename's trailing characters already consumed during recursion. 83*f4a2713aSLionel Sambuc /// 84*f4a2713aSLionel Sambuc /// To find the best matching node for a given path 'p', the 85*f4a2713aSLionel Sambuc /// \c findEquivalent() function is called recursively for each path segment 86*f4a2713aSLionel Sambuc /// (back to fron) of 'p' until a node 'n' is reached that does not .. 87*f4a2713aSLionel Sambuc /// - .. have children. In this case it is checked 88*f4a2713aSLionel Sambuc /// whether the stored path is equivalent to 'p'. If yes, the best match is 89*f4a2713aSLionel Sambuc /// found. Otherwise continue with the parent node as if this node did not 90*f4a2713aSLionel Sambuc /// exist. 91*f4a2713aSLionel Sambuc /// - .. a child matching the next path segment. In this case, all children of 92*f4a2713aSLionel Sambuc /// 'n' are an equally good match for 'p'. All children are of 'n' are found 93*f4a2713aSLionel Sambuc /// recursively and their equivalence to 'p' is determined. If none are 94*f4a2713aSLionel Sambuc /// equivalent, continue with the parent node as if 'n' didn't exist. If one 95*f4a2713aSLionel Sambuc /// is equivalent, the best match is found. Otherwise, report and ambigiuity 96*f4a2713aSLionel Sambuc /// error. 97*f4a2713aSLionel Sambuc StringRef findEquivalent(const PathComparator& Comparator, 98*f4a2713aSLionel Sambuc StringRef FileName, 99*f4a2713aSLionel Sambuc bool &IsAmbiguous, 100*f4a2713aSLionel Sambuc unsigned ConsumedLength = 0) const { 101*f4a2713aSLionel Sambuc if (Children.empty()) { 102*f4a2713aSLionel Sambuc if (Comparator.equivalent(StringRef(Path), FileName)) 103*f4a2713aSLionel Sambuc return StringRef(Path); 104*f4a2713aSLionel Sambuc return StringRef(); 105*f4a2713aSLionel Sambuc } 106*f4a2713aSLionel Sambuc StringRef Element(llvm::sys::path::filename(FileName.drop_back( 107*f4a2713aSLionel Sambuc ConsumedLength))); 108*f4a2713aSLionel Sambuc llvm::StringMap<FileMatchTrieNode>::const_iterator MatchingChild = 109*f4a2713aSLionel Sambuc Children.find(Element); 110*f4a2713aSLionel Sambuc if (MatchingChild != Children.end()) { 111*f4a2713aSLionel Sambuc StringRef Result = MatchingChild->getValue().findEquivalent( 112*f4a2713aSLionel Sambuc Comparator, FileName, IsAmbiguous, 113*f4a2713aSLionel Sambuc ConsumedLength + Element.size() + 1); 114*f4a2713aSLionel Sambuc if (!Result.empty() || IsAmbiguous) 115*f4a2713aSLionel Sambuc return Result; 116*f4a2713aSLionel Sambuc } 117*f4a2713aSLionel Sambuc std::vector<StringRef> AllChildren; 118*f4a2713aSLionel Sambuc getAll(AllChildren, MatchingChild); 119*f4a2713aSLionel Sambuc StringRef Result; 120*f4a2713aSLionel Sambuc for (unsigned i = 0; i < AllChildren.size(); i++) { 121*f4a2713aSLionel Sambuc if (Comparator.equivalent(AllChildren[i], FileName)) { 122*f4a2713aSLionel Sambuc if (Result.empty()) { 123*f4a2713aSLionel Sambuc Result = AllChildren[i]; 124*f4a2713aSLionel Sambuc } else { 125*f4a2713aSLionel Sambuc IsAmbiguous = true; 126*f4a2713aSLionel Sambuc return StringRef(); 127*f4a2713aSLionel Sambuc } 128*f4a2713aSLionel Sambuc } 129*f4a2713aSLionel Sambuc } 130*f4a2713aSLionel Sambuc return Result; 131*f4a2713aSLionel Sambuc } 132*f4a2713aSLionel Sambuc 133*f4a2713aSLionel Sambuc private: 134*f4a2713aSLionel Sambuc /// \brief Gets all paths under this FileMatchTrieNode. 135*f4a2713aSLionel Sambuc void getAll(std::vector<StringRef> &Results, 136*f4a2713aSLionel Sambuc llvm::StringMap<FileMatchTrieNode>::const_iterator Except) const { 137*f4a2713aSLionel Sambuc if (Path.empty()) 138*f4a2713aSLionel Sambuc return; 139*f4a2713aSLionel Sambuc if (Children.empty()) { 140*f4a2713aSLionel Sambuc Results.push_back(StringRef(Path)); 141*f4a2713aSLionel Sambuc return; 142*f4a2713aSLionel Sambuc } 143*f4a2713aSLionel Sambuc for (llvm::StringMap<FileMatchTrieNode>::const_iterator 144*f4a2713aSLionel Sambuc It = Children.begin(), E = Children.end(); 145*f4a2713aSLionel Sambuc It != E; ++It) { 146*f4a2713aSLionel Sambuc if (It == Except) 147*f4a2713aSLionel Sambuc continue; 148*f4a2713aSLionel Sambuc It->getValue().getAll(Results, Children.end()); 149*f4a2713aSLionel Sambuc } 150*f4a2713aSLionel Sambuc } 151*f4a2713aSLionel Sambuc 152*f4a2713aSLionel Sambuc // The stored absolute path in this node. Only valid for leaf nodes, i.e. 153*f4a2713aSLionel Sambuc // nodes where Children.empty(). 154*f4a2713aSLionel Sambuc std::string Path; 155*f4a2713aSLionel Sambuc 156*f4a2713aSLionel Sambuc // The children of this node stored in a map based on the next path segment. 157*f4a2713aSLionel Sambuc llvm::StringMap<FileMatchTrieNode> Children; 158*f4a2713aSLionel Sambuc }; 159*f4a2713aSLionel Sambuc 160*f4a2713aSLionel Sambuc FileMatchTrie::FileMatchTrie() 161*f4a2713aSLionel Sambuc : Root(new FileMatchTrieNode), Comparator(new DefaultPathComparator()) {} 162*f4a2713aSLionel Sambuc 163*f4a2713aSLionel Sambuc FileMatchTrie::FileMatchTrie(PathComparator *Comparator) 164*f4a2713aSLionel Sambuc : Root(new FileMatchTrieNode), Comparator(Comparator) {} 165*f4a2713aSLionel Sambuc 166*f4a2713aSLionel Sambuc FileMatchTrie::~FileMatchTrie() { 167*f4a2713aSLionel Sambuc delete Root; 168*f4a2713aSLionel Sambuc } 169*f4a2713aSLionel Sambuc 170*f4a2713aSLionel Sambuc void FileMatchTrie::insert(StringRef NewPath) { 171*f4a2713aSLionel Sambuc Root->insert(NewPath); 172*f4a2713aSLionel Sambuc } 173*f4a2713aSLionel Sambuc 174*f4a2713aSLionel Sambuc StringRef FileMatchTrie::findEquivalent(StringRef FileName, 175*f4a2713aSLionel Sambuc raw_ostream &Error) const { 176*f4a2713aSLionel Sambuc if (llvm::sys::path::is_relative(FileName)) { 177*f4a2713aSLionel Sambuc Error << "Cannot resolve relative paths"; 178*f4a2713aSLionel Sambuc return StringRef(); 179*f4a2713aSLionel Sambuc } 180*f4a2713aSLionel Sambuc bool IsAmbiguous = false; 181*f4a2713aSLionel Sambuc StringRef Result = Root->findEquivalent(*Comparator, FileName, IsAmbiguous); 182*f4a2713aSLionel Sambuc if (IsAmbiguous) 183*f4a2713aSLionel Sambuc Error << "Path is ambiguous"; 184*f4a2713aSLionel Sambuc return Result; 185*f4a2713aSLionel Sambuc } 186*f4a2713aSLionel Sambuc 187*f4a2713aSLionel Sambuc } // end namespace tooling 188*f4a2713aSLionel Sambuc } // end namespace clang 189