This PR implements a CC1 flag `-dump-minimization-hints`.
The flag allows to specify a file path to dump ranges of deserialized
declarations in `ASTReader`. Example usage:
```
clang -Xclang=-dump-minimization-hints=/tmp/decls -c file.cc -o file.o
```
Example output:
```
// /tmp/decls
{
"required_ranges": [
{
"file": "foo.h",
"range": [
{
"from": {
"line": 26,
"column": 1
},
"to": {
"line": 27,
"column": 77
}
}
]
},
{
"file": "bar.h",
"range": [
{
"from": {
"line": 30,
"column": 1
},
"to": {
"line": 35,
"column": 1
}
},
{
"from": {
"line": 92,
"column": 1
},
"to": {
"line": 95,
"column": 1
}
}
]
}
]
}
```
Specifying the flag creates an instance of
`DeserializedDeclsSourceRangePrinter`, which dumps ranges of deserialized
declarations to aid debugging and bug minimization (we use is as input to [C-Vise](https://github.com/emaxx-google/cvise/tree/multifile-hints).
Required ranges are computed from source ranges of Decls.
`TranslationUnitDecl`, `LinkageSpecDecl` and `NamespaceDecl` are ignored
for the sake of this PR.
Technical details:
* `DeserializedDeclsSourceRangePrinter` implements `ASTConsumer` and
`ASTDeserializationListener`, so that an object of
`DeserializedDeclsSourceRangePrinter` registers as its own listener.
* `ASTDeserializationListener` interface provides the `DeclRead`
callback that we use to collect the deserialized Decls.
Printing or otherwise processing them as this point is dangerous, since
that could trigger additional deserialization and crash compilation.
* The collected Decls are processed in `HandleTranslationUnit` method of
`ASTConsumer`. This is a safe point, since we know that by this point
all the Decls needed by the compiler frontend have been deserialized.
* In case our processing causes further deserialization, `DeclRead` from
the listener might be called again. However, at that point we don't
accept any more Decls for processing.
80 lines
2.0 KiB
C++
80 lines
2.0 KiB
C++
// RUN: rm -rf %t
|
|
// RUN: mkdir -p %t
|
|
// RUN: split-file %s %t
|
|
// RUN: %clang_cc1 -xc++ -fmodules -fmodule-name=foo -fmodule-map-file=%t/foo.cppmap -emit-module %t/foo.cppmap -o %t/foo.pcm
|
|
// RUN: %clang_cc1 -xc++ -fmodules -dump-minimization-hints=%t/decls -fmodule-file=%t/foo.pcm %t/foo.cpp -o %t/foo.o
|
|
// RUN: cat %t/decls
|
|
// RUN: cat %t/decls | FileCheck -check-prefix=RANGE %s
|
|
// RANGE:{
|
|
// RANGE-NEXT: "required_ranges": [
|
|
// RANGE-NEXT: {
|
|
// RANGE-NEXT: "file": "{{.+}}foo.h",
|
|
// RANGE-NEXT: "range": [
|
|
// RANGE-NEXT: {
|
|
// RANGE-NEXT: "from": {
|
|
// RANGE-NEXT: "line": 1,
|
|
// RANGE-NEXT: "column": 1
|
|
// RANGE-NEXT: },
|
|
// RANGE-NEXT: "to": {
|
|
// RANGE-NEXT: "line": 9,
|
|
// RANGE-NEXT: "column": 3
|
|
// RANGE-NEXT: }
|
|
// RANGE-NEXT: },
|
|
// RANGE-NEXT: {
|
|
// RANGE-NEXT: "from": {
|
|
// RANGE-NEXT: "line": 11,
|
|
// RANGE-NEXT: "column": 1
|
|
// RANGE-NEXT: },
|
|
// RANGE-NEXT: "to": {
|
|
// RANGE-NEXT: "line": 11,
|
|
// RANGE-NEXT: "column": 25
|
|
// RANGE-NEXT: }
|
|
// RANGE-NEXT: },
|
|
// RANGE-NEXT: {
|
|
// RANGE-NEXT: "from": {
|
|
// RANGE-NEXT: "line": 13,
|
|
// RANGE-NEXT: "column": 1
|
|
// RANGE-NEXT: },
|
|
// RANGE-NEXT: "to": {
|
|
// RANGE-NEXT: "line": 15,
|
|
// RANGE-NEXT: "column": 2
|
|
// RANGE-NEXT: }
|
|
// RANGE-NEXT: }
|
|
// RANGE-NEXT: ]
|
|
// RANGE-NEXT: }
|
|
// RANGE-NEXT: ]
|
|
// RANGE-NEXT:}
|
|
|
|
//--- foo.cppmap
|
|
module foo {
|
|
header "foo.h"
|
|
export *
|
|
}
|
|
|
|
//--- foo.h
|
|
class MyData {
|
|
public:
|
|
MyData(int val): value_(val) {}
|
|
int getValue() const {
|
|
return 5;
|
|
}
|
|
private:
|
|
int value_;
|
|
};
|
|
|
|
extern int global_value;
|
|
|
|
int multiply(int a, int b) {
|
|
return a * b;
|
|
}
|
|
|
|
//--- foo.cpp
|
|
#include "foo.h"
|
|
int global_value = 5;
|
|
int main() {
|
|
MyData data(5);
|
|
int current_value = data.getValue();
|
|
int doubled_value = multiply(current_value, 2);
|
|
int final_result = doubled_value + global_value;
|
|
}
|