
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;
|
|
}
|