2525
2626#include < cstring>
2727#include < exception>
28+ #include < iostream>
2829#include < map>
2930#include < sstream>
3031#include < utility>
@@ -82,29 +83,55 @@ void AnalyzerInformation::close()
8283 }
8384}
8485
85- bool AnalyzerInformation::skipAnalysis (const tinyxml2::XMLDocument &analyzerInfoDoc, std::size_t hash, std::list<ErrorMessage> &errors)
86+ bool AnalyzerInformation::skipAnalysis (const tinyxml2::XMLDocument &analyzerInfoDoc, std::size_t hash, std::list<ErrorMessage> &errors, bool debug )
8687{
8788 const tinyxml2::XMLElement * const rootNode = analyzerInfoDoc.FirstChildElement ();
88- if (rootNode == nullptr )
89+ if (rootNode == nullptr ) {
90+ if (debug)
91+ std::cout << " discarding cached result - no root node found" << std::endl;
8992 return false ;
93+ }
9094
91- const char *attr = rootNode->Attribute (" hash" );
92- if (!attr || attr != std::to_string (hash))
95+ if (strcmp (rootNode->Name (), " analyzerinfo" ) != 0 ) {
96+ if (debug)
97+ std::cout << " discarding cached result - unexpected root node" << std::endl;
9398 return false ;
99+ }
94100
95- // Check for invalid license error or internal error, in which case we should retry analysis
96- for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement (); e; e = e->NextSiblingElement ()) {
97- if (std::strcmp (e->Name (), " error" ) == 0 &&
98- (e->Attribute (" id" , " premium-invalidLicense" ) ||
99- e->Attribute (" id" , " premium-internalError" ) ||
100- e->Attribute (" id" , " internalError" )
101- ))
102- return false ;
101+ const char * const attr = rootNode->Attribute (" hash" );
102+ if (!attr) {
103+ if (debug)
104+ std::cout << " discarding cached result - no 'hash' attribute found" << std::endl;
105+ return false ;
106+ }
107+ if (attr != std::to_string (hash)) {
108+ if (debug)
109+ std::cout << " discarding cached result - hash mismatch" << std::endl;
110+ return false ;
103111 }
104112
105113 for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement (); e; e = e->NextSiblingElement ()) {
106- if (std::strcmp (e->Name (), " error" ) == 0 )
107- errors.emplace_back (e);
114+ if (std::strcmp (e->Name (), " error" ) != 0 )
115+ continue ;
116+
117+ // TODO: discarding results on internalError doesn't make sense since that won't fix itself
118+ // Check for invalid license error or internal error, in which case we should retry analysis
119+ static std::array<const char *, 3 > s_ids{
120+ " premium-invalidLicense" ,
121+ " premium-internalError" ,
122+ " internalError"
123+ };
124+ for (const auto * id : s_ids)
125+ {
126+ if (e->Attribute (" id" , id)) {
127+ if (debug)
128+ std::cout << " discarding cached result - '" << id << " ' encountered" << std::endl;
129+ errors.clear ();
130+ return false ;
131+ }
132+ }
133+
134+ errors.emplace_back (e);
108135 }
109136
110137 return true ;
@@ -138,27 +165,43 @@ std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir
138165 filename = sourcefile;
139166 else
140167 filename = sourcefile.substr (pos + 1 );
168+ // TODO: is this correct? the above code will return files ending in '.aN'. Also does not consider the ID
141169 return Path::join (buildDir, std::move (filename)) + " .analyzerinfo" ;
142170}
143171
144- bool AnalyzerInformation::analyzeFile (const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId, std::size_t hash, std::list<ErrorMessage> &errors)
172+ bool AnalyzerInformation::analyzeFile (const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId, std::size_t hash, std::list<ErrorMessage> &errors, bool debug )
145173{
174+ if (mOutputStream .is_open ())
175+ throw std::runtime_error (" analyzer information file is already open" );
176+
146177 if (buildDir.empty () || sourcefile.empty ())
147178 return true ;
148- close ();
149179
150180 const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile (buildDir,sourcefile,cfg,fsFileId);
151181
152- tinyxml2::XMLDocument analyzerInfoDoc;
153- const tinyxml2::XMLError xmlError = analyzerInfoDoc.LoadFile (analyzerInfoFile.c_str ());
154- if (xmlError == tinyxml2::XML_SUCCESS && skipAnalysis (analyzerInfoDoc, hash, errors))
155- return false ;
182+ {
183+ tinyxml2::XMLDocument analyzerInfoDoc;
184+ const tinyxml2::XMLError xmlError = analyzerInfoDoc.LoadFile (analyzerInfoFile.c_str ());
185+ if (xmlError == tinyxml2::XML_SUCCESS) {
186+ if (skipAnalysis (analyzerInfoDoc, hash, errors, debug)) {
187+ if (debug)
188+ std::cout << " skipping analysis - loaded " << errors.size () << " cached finding(s) from '" << analyzerInfoFile << " '" << std::endl;
189+ return false ;
190+ }
191+ }
192+ else if (xmlError != tinyxml2::XML_ERROR_FILE_NOT_FOUND) {
193+ if (debug)
194+ std::cout << " discarding cached result - failed to load '" << analyzerInfoFile << " ' (" << tinyxml2::XMLDocument::ErrorIDToName (xmlError) << " )" << std::endl;
195+ }
196+ else if (debug)
197+ std::cout << " no cached result '" << analyzerInfoFile << " ' found" << std::endl;
198+ }
156199
157200 mOutputStream .open (analyzerInfoFile);
158- if (mOutputStream .is_open ()) {
159- mOutputStream << " <?xml version= \" 1.0 \" ?> \n " ;
160- mOutputStream << " <analyzerinfo hash =\" " << hash << " \" >\n " ;
161- }
201+ if (! mOutputStream .is_open ())
202+ throw std::runtime_error ( " failed to open ' " + analyzerInfoFile + " ' " ) ;
203+ mOutputStream << " <?xml version =\" 1.0 \" ? >\n " ;
204+ mOutputStream << " <analyzerinfo hash= \" " << hash << " \" > \n " ;
162205
163206 return true ;
164207}
@@ -175,6 +218,7 @@ void AnalyzerInformation::setFileInfo(const std::string &check, const std::strin
175218 mOutputStream << " <FileInfo check=\" " << check << " \" >\n " << fileInfo << " </FileInfo>\n " ;
176219}
177220
221+ // TODO: report detailed errors?
178222bool AnalyzerInformation::Info::parse (const std::string& filesTxtLine) {
179223 const std::string::size_type sep1 = filesTxtLine.find (sep);
180224 if (sep1 == std::string::npos)
@@ -202,37 +246,58 @@ bool AnalyzerInformation::Info::parse(const std::string& filesTxtLine) {
202246 return true ;
203247}
204248
205- // TODO: bail out on unexpected data
206- void AnalyzerInformation::processFilesTxt (const std::string& buildDir, const std::function<void (const char * checkattr, const tinyxml2::XMLElement* e, const Info& filesTxtInfo)>& handler)
249+ std::string AnalyzerInformation::processFilesTxt (const std::string& buildDir, const std::function<void (const char * checkattr, const tinyxml2::XMLElement* e, const Info& filesTxtInfo)>& handler, bool debug)
207250{
208251 const std::string filesTxt (buildDir + " /files.txt" );
209252 std::ifstream fin (filesTxt.c_str ());
210253 std::string filesTxtLine;
211254 while (std::getline (fin, filesTxtLine)) {
212255 AnalyzerInformation::Info filesTxtInfo;
213- if (!filesTxtInfo.parse (filesTxtLine)) {
214- return ;
215- }
256+ if (!filesTxtInfo.parse (filesTxtLine))
257+ return " failed to parse '" + filesTxtLine + " ' from '" + filesTxt + " '" ;
258+
259+ if (filesTxtInfo.afile .empty ())
260+ return " empty afile from '" + filesTxt + " '" ;
216261
217262 const std::string xmlfile = buildDir + ' /' + filesTxtInfo.afile ;
218263
219264 tinyxml2::XMLDocument doc;
220265 const tinyxml2::XMLError error = doc.LoadFile (xmlfile.c_str ());
266+ if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) {
267+ /* FIXME: this can currently not be reported as an error because:
268+ * - --clang does not generate any analyzer information - see #14456
269+ * - markup files might not generate analyzer information
270+ * - files with preprocessor errors might not generate analyzer information
271+ */
272+ if (debug)
273+ std::cout << " '" + xmlfile + " ' from '" + filesTxt + " ' not found" ;
274+ return " " ;
275+ }
276+
221277 if (error != tinyxml2::XML_SUCCESS)
222- return ;
278+ return " failed to load ' " + xmlfile + " ' from ' " + filesTxt + " ' " ;
223279
224280 const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement ();
225281 if (rootNode == nullptr )
226- return ;
282+ return " no root node found in '" + xmlfile + " ' from '" + filesTxt + " '" ;
283+
284+ if (strcmp (rootNode->Name (), " analyzerinfo" ) != 0 )
285+ return " unexpected root node in '" + xmlfile + " ' from '" + filesTxt + " '" ;
227286
228287 for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement (); e; e = e->NextSiblingElement ()) {
229288 if (std::strcmp (e->Name (), " FileInfo" ) != 0 )
230289 continue ;
231290 const char *checkattr = e->Attribute (" check" );
232- if (checkattr == nullptr )
291+ if (checkattr == nullptr ) {
292+ if (debug)
293+ std::cout << " 'check' attribute missing in 'FileInfo' in '" << xmlfile << " ' from '" << filesTxt + " '" ;
233294 continue ;
295+ }
234296 handler (checkattr, e, filesTxtInfo);
235297 }
236298 }
299+
300+ // TODO: error on empty file?
301+ return " " ;
237302}
238303
0 commit comments