Skip to content

Commit 333d66b

Browse files
Snape3058Balazs Benics
authored andcommitted
[analyzer][ctu] Fix wrong 'multiple definitions' errors caused by space characters in lookup names when parsing the ctu index file
This error was found when analyzing MySQL with CTU enabled. When there are space characters in the lookup name, the current delimiter searching strategy will make the file path wrongly parsed. And when two lookup names have the same prefix before their first space characters, a 'multiple definitions' error will be wrongly reported. e.g. The lookup names for the two lambda exprs in the test case are `c:@s@G@F@G#@sa@F@operator int (*)(char)#1` and `c:@s@G@F@G#@sa@F@operator bool (*)(char)#1` respectively. And their prefixes are both `c:@s@G@F@G#@sa@F@operator` when using the first space character as the delimiter. Solving the problem by adding a length for the lookup name, making the index items in the format of `USR-Length:USR File-Path`. Reviewed By: steakhal Differential Revision: https://reviews.llvm.org/D102669
1 parent 3a1eb1c commit 333d66b

12 files changed

+154
-75
lines changed

clang/docs/analyzer/user-docs/CrossTranslationUnit.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@ of `foo.cpp`:
8181
$
8282
8383
The next step is to create a CTU index file which holds the `USR` name and location of external definitions in the
84-
source files:
84+
source files in format `<USR-Length>:<USR> <File-Path>`:
8585

8686
.. code-block:: bash
8787
8888
$ clang-extdef-mapping -p . foo.cpp
89-
c:@F@foo# /path/to/your/project/foo.cpp
89+
9:c:@F@foo# /path/to/your/project/foo.cpp
9090
$ clang-extdef-mapping -p . foo.cpp > externalDefMap.txt
9191
9292
We have to modify `externalDefMap.txt` to contain the name of the `.ast` files instead of the source files:
@@ -278,12 +278,12 @@ The `invocation list`:
278278
279279
We'd like to analyze `main.cpp` and discover the division by zero bug.
280280
As we are using On-demand mode, we only need to create a CTU index file which holds the `USR` name and location of
281-
external definitions in the source files:
281+
external definitions in the source files in format `<USR-Length>:<USR> <File-Path>`:
282282

283283
.. code-block:: bash
284284
285285
$ clang-extdef-mapping -p . foo.cpp
286-
c:@F@foo# /path/to/your/project/foo.cpp
286+
9:c:@F@foo# /path/to/your/project/foo.cpp
287287
$ clang-extdef-mapping -p . foo.cpp > externalDefMap.txt
288288
289289
Now everything is available for the CTU analysis.

clang/include/clang/Basic/DiagnosticCrossTUKinds.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ def err_ctu_error_opening : Error<
1212
"error opening '%0': required by the CrossTU functionality">;
1313

1414
def err_extdefmap_parsing : Error<
15-
"error parsing index file: '%0' line: %1 'UniqueID filename' format "
16-
"expected">;
15+
"error parsing index file: '%0' line: %1 '<USR-Length>:<USR> <File-Path>' "
16+
"format expected">;
1717

1818
def err_multiple_def_index : Error<
1919
"multiple definitions are found for the same key in index ">;

clang/lib/CrossTU/CrossTranslationUnit.cpp

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,35 @@ std::error_code IndexError::convertToErrorCode() const {
149149
return std::error_code(static_cast<int>(Code), *Category);
150150
}
151151

152+
/// Parse one line of the input CTU index file.
153+
///
154+
/// @param[in] LineRef The input CTU index item in format
155+
/// "<USR-Length>:<USR> <File-Path>".
156+
/// @param[out] LookupName The lookup name in format "<USR-Length>:<USR>".
157+
/// @param[out] FilePath The file path "<File-Path>".
158+
static bool parseCrossTUIndexItem(StringRef LineRef, StringRef &LookupName,
159+
StringRef &FilePath) {
160+
// `LineRef` is "<USR-Length>:<USR> <File-Path>" now.
161+
162+
size_t USRLength = 0;
163+
if (LineRef.consumeInteger(10, USRLength))
164+
return false;
165+
assert(USRLength && "USRLength should be greater than zero.");
166+
167+
if (!LineRef.consume_front(":"))
168+
return false;
169+
170+
// `LineRef` is now just "<USR> <File-Path>".
171+
172+
// Check LookupName length out of bound and incorrect delimiter.
173+
if (USRLength >= LineRef.size() || ' ' != LineRef[USRLength])
174+
return false;
175+
176+
LookupName = LineRef.substr(0, USRLength);
177+
FilePath = LineRef.substr(USRLength + 1);
178+
return true;
179+
}
180+
152181
llvm::Expected<llvm::StringMap<std::string>>
153182
parseCrossTUIndex(StringRef IndexPath) {
154183
std::ifstream ExternalMapFile{std::string(IndexPath)};
@@ -160,24 +189,23 @@ parseCrossTUIndex(StringRef IndexPath) {
160189
std::string Line;
161190
unsigned LineNo = 1;
162191
while (std::getline(ExternalMapFile, Line)) {
163-
StringRef LineRef{Line};
164-
const size_t Delimiter = LineRef.find(' ');
165-
if (Delimiter > 0 && Delimiter != std::string::npos) {
166-
StringRef LookupName = LineRef.substr(0, Delimiter);
167-
168-
// Store paths with posix-style directory separator.
169-
SmallString<32> FilePath(LineRef.substr(Delimiter + 1));
170-
llvm::sys::path::native(FilePath, llvm::sys::path::Style::posix);
171-
172-
bool InsertionOccured;
173-
std::tie(std::ignore, InsertionOccured) =
174-
Result.try_emplace(LookupName, FilePath.begin(), FilePath.end());
175-
if (!InsertionOccured)
176-
return llvm::make_error<IndexError>(
177-
index_error_code::multiple_definitions, IndexPath.str(), LineNo);
178-
} else
192+
// Split lookup name and file path
193+
StringRef LookupName, FilePathInIndex;
194+
if (!parseCrossTUIndexItem(Line, LookupName, FilePathInIndex))
179195
return llvm::make_error<IndexError>(
180196
index_error_code::invalid_index_format, IndexPath.str(), LineNo);
197+
198+
// Store paths with posix-style directory separator.
199+
SmallString<32> FilePath(FilePathInIndex);
200+
llvm::sys::path::native(FilePath, llvm::sys::path::Style::posix);
201+
202+
bool InsertionOccured;
203+
std::tie(std::ignore, InsertionOccured) =
204+
Result.try_emplace(LookupName, FilePath.begin(), FilePath.end());
205+
if (!InsertionOccured)
206+
return llvm::make_error<IndexError>(
207+
index_error_code::multiple_definitions, IndexPath.str(), LineNo);
208+
181209
++LineNo;
182210
}
183211
return Result;
@@ -187,7 +215,8 @@ std::string
187215
createCrossTUIndexString(const llvm::StringMap<std::string> &Index) {
188216
std::ostringstream Result;
189217
for (const auto &E : Index)
190-
Result << E.getKey().str() << " " << E.getValue() << '\n';
218+
Result << E.getKey().size() << ':' << E.getKey().str() << ' '
219+
<< E.getValue() << '\n';
191220
return Result.str();
192221
}
193222

@@ -428,7 +457,7 @@ CrossTranslationUnitContext::ASTUnitStorage::getASTUnitForFunction(
428457
ensureCTUIndexLoaded(CrossTUDir, IndexName))
429458
return std::move(IndexLoadError);
430459

431-
// Check if there is and entry in the index for the function.
460+
// Check if there is an entry in the index for the function.
432461
if (!NameFileMap.count(FunctionName)) {
433462
++NumNotInOtherTU;
434463
return llvm::make_error<IndexError>(index_error_code::missing_definition);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
c:@F@testStaticImplicit ctu-import.c.ast
1+
23:c:@F@testStaticImplicit ctu-import.c.ast
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
void f(void (*)());
2+
void f(void (*)(int));
3+
4+
struct G {
5+
G() {
6+
// multiple definitions are found for the same key in index
7+
f([]() -> void {}); // USR: c:@S@G@F@G#@Sa@F@operator void (*)()#1
8+
f([](int) -> void {}); // USR: c:@S@G@F@G#@Sa@F@operator void (*)(int)#1
9+
10+
// As both lambda exprs have the same prefix, if the CTU index parser uses
11+
// the first space character as the delimiter between USR and file path, a
12+
// "multiple definitions are found for the same key in index" error will
13+
// be reported.
14+
}
15+
};
16+
17+
void importee() {}
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
c:@F@inlineAsm ctu-other.c.ast
2-
c:@F@g ctu-other.c.ast
3-
c:@F@f ctu-other.c.ast
4-
c:@F@enumCheck ctu-other.c.ast
5-
c:@F@identImplicit ctu-other.c.ast
6-
c:@F@structInProto ctu-other.c.ast
7-
c:@F@switchWithoutCases ctu-other.c.ast
1+
14:c:@F@inlineAsm ctu-other.c.ast
2+
6:c:@F@g ctu-other.c.ast
3+
6:c:@F@f ctu-other.c.ast
4+
14:c:@F@enumCheck ctu-other.c.ast
5+
18:c:@F@identImplicit ctu-other.c.ast
6+
18:c:@F@structInProto ctu-other.c.ast
7+
23:c:@F@switchWithoutCases ctu-other.c.ast
Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
1-
c:@N@chns@F@chf1#I# ctu-other.cpp.ast
2-
c:@N@myns@N@embed_ns@F@fens#I# ctu-other.cpp.ast
3-
c:@F@g#I# ctu-other.cpp.ast
4-
c:@S@mycls@F@fscl#I#S ctu-other.cpp.ast
5-
c:@S@mycls@F@fcl#I# ctu-other.cpp.ast
6-
c:@S@mycls@F@fvcl#I# ctu-other.cpp.ast
7-
c:@N@myns@S@embed_cls@F@fecl#I# ctu-other.cpp.ast
8-
c:@S@mycls@S@embed_cls2@F@fecl2#I# ctu-other.cpp.ast
9-
c:@S@derived@F@fvcl#I# ctu-other.cpp.ast
10-
c:@F@f#I# ctu-other.cpp.ast
11-
c:@N@myns@F@fns#I# ctu-other.cpp.ast
12-
c:@F@h#I# ctu-other.cpp.ast
13-
c:@F@h_chain#I# ctu-chain.cpp.ast
14-
c:@N@chns@S@chcls@F@chf4#I# ctu-chain.cpp.ast
15-
c:@N@chns@F@chf2#I# ctu-chain.cpp.ast
16-
c:@F@fun_using_anon_struct#I# ctu-other.cpp.ast
17-
c:@F@other_macro_diag#I# ctu-other.cpp.ast
18-
c:@extInt ctu-other.cpp.ast
19-
c:@N@intns@extInt ctu-other.cpp.ast
20-
c:@extS ctu-other.cpp.ast
21-
c:@S@A@a ctu-other.cpp.ast
22-
c:@extSC ctu-other.cpp.ast
23-
c:@S@ST@sc ctu-other.cpp.ast
24-
c:@extSCN ctu-other.cpp.ast
25-
c:@extSubSCN ctu-other.cpp.ast
26-
c:@extSCC ctu-other.cpp.ast
27-
c:@extU ctu-other.cpp.ast
28-
c:@S@TestAnonUnionUSR@Test ctu-other.cpp.ast
29-
c:@F@testImportOfIncompleteDefaultParmDuringImport#I# ctu-other.cpp.ast
30-
c:@F@testImportOfDelegateConstructor#I# ctu-other.cpp.ast
1+
19:c:@N@chns@F@chf1#I# ctu-other.cpp.ast
2+
30:c:@N@myns@N@embed_ns@F@fens#I# ctu-other.cpp.ast
3+
9:c:@F@g#I# ctu-other.cpp.ast
4+
21:c:@S@mycls@F@fscl#I#S ctu-other.cpp.ast
5+
19:c:@S@mycls@F@fcl#I# ctu-other.cpp.ast
6+
20:c:@S@mycls@F@fvcl#I# ctu-other.cpp.ast
7+
31:c:@N@myns@S@embed_cls@F@fecl#I# ctu-other.cpp.ast
8+
34:c:@S@mycls@S@embed_cls2@F@fecl2#I# ctu-other.cpp.ast
9+
22:c:@S@derived@F@fvcl#I# ctu-other.cpp.ast
10+
9:c:@F@f#I# ctu-other.cpp.ast
11+
18:c:@N@myns@F@fns#I# ctu-other.cpp.ast
12+
9:c:@F@h#I# ctu-other.cpp.ast
13+
15:c:@F@h_chain#I# ctu-chain.cpp.ast
14+
27:c:@N@chns@S@chcls@F@chf4#I# ctu-chain.cpp.ast
15+
19:c:@N@chns@F@chf2#I# ctu-chain.cpp.ast
16+
29:c:@F@fun_using_anon_struct#I# ctu-other.cpp.ast
17+
24:c:@F@other_macro_diag#I# ctu-other.cpp.ast
18+
9:c:@extInt ctu-other.cpp.ast
19+
17:c:@N@intns@extInt ctu-other.cpp.ast
20+
7:c:@extS ctu-other.cpp.ast
21+
8:c:@S@A@a ctu-other.cpp.ast
22+
8:c:@extSC ctu-other.cpp.ast
23+
10:c:@S@ST@sc ctu-other.cpp.ast
24+
9:c:@extSCN ctu-other.cpp.ast
25+
12:c:@extSubSCN ctu-other.cpp.ast
26+
9:c:@extSCC ctu-other.cpp.ast
27+
7:c:@extU ctu-other.cpp.ast
28+
26:c:@S@TestAnonUnionUSR@Test ctu-other.cpp.ast
29+
53:c:@F@testImportOfIncompleteDefaultParmDuringImport#I# ctu-other.cpp.ast
30+
39:c:@F@testImportOfDelegateConstructor#I# ctu-other.cpp.ast
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
c:@F@F1 plist-macros-ctu.c.ast
2-
c:@F@F2 plist-macros-ctu.c.ast
3-
c:@F@F3 plist-macros-ctu.c.ast
4-
c:@F@F_H plist-macros-ctu.c.ast
1+
7:c:@F@F1 plist-macros-ctu.c.ast
2+
7:c:@F@F2 plist-macros-ctu.c.ast
3+
7:c:@F@F3 plist-macros-ctu.c.ast
4+
8:c:@F@F_H plist-macros-ctu.c.ast

clang/test/Analysis/ctu-inherited-default-ctor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// RUN: %clang_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
55
// RUN: -emit-pch -o %t/ctudir/ctu-inherited-default-ctor-other.cpp.ast \
66
// RUN: %S/Inputs/ctu-inherited-default-ctor-other.cpp
7-
// RUN: echo "c:@N@clang@S@DeclContextLookupResult@SingleElementDummyList ctu-inherited-default-ctor-other.cpp.ast" \
7+
// RUN: echo "59:c:@N@clang@S@DeclContextLookupResult@SingleElementDummyList ctu-inherited-default-ctor-other.cpp.ast" \
88
// RUN: > %t/ctudir/externalDefMap.txt
99
//
1010
// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir %t
3+
4+
// RUN: echo '"%/S/Inputs/ctu-lookup-name-with-space.cpp" : ["g++", "-c", "%/S/Inputs/ctu-lookup-name-with-space.cpp"]' > %t/invocations.yaml
5+
// RUN: %clang_extdef_map %S/Inputs/ctu-lookup-name-with-space.cpp -- > %t/externalDefMap.txt
6+
7+
// RUN: cd %t && %clang_cc1 -fsyntax-only -analyze \
8+
// RUN: -analyzer-checker=core,debug.ExprInspection \
9+
// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \
10+
// RUN: -analyzer-config ctu-dir=. \
11+
// RUN: -analyzer-config ctu-invocation-list=invocations.yaml \
12+
// RUN: -verify %s
13+
14+
void importee();
15+
16+
void trigger() {
17+
// Call an external function to trigger the parsing process of CTU index.
18+
// Refer to file Inputs/ctu-lookup-name-with-space.cpp for more details.
19+
20+
importee(); // expected-no-diagnostics
21+
}

0 commit comments

Comments
 (0)