adamcz created this revision.
adamcz added a reviewer: sammccall.
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, jkorous, 
MaskRay, ilya-biryukov.
Herald added a project: clang.

When preamble contains #undef, indexing code finds the matching #define
and uses that during indexing. However, it would only look for local
definitions. If the macro was defined in a module, MacroInfo
would be nullptr and clangd would crash.

This change fixes the crash by looking for definition in module when
that happens. The indexing result is then exactly the same whether
modules are used or not.

The indexing of macros happens for preamble only, so then #undef must be
in the preamble, which is why we need two .h files in a test.

Note that clangd is currently not ready for module support, but this
brings us one step closer.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80525

Files:
  clang-tools-extra/clangd/test/Inputs/symbols-modules/bar.h
  clang-tools-extra/clangd/test/Inputs/symbols-modules/compile_commands.json
  clang-tools-extra/clangd/test/Inputs/symbols-modules/foo.h
  clang-tools-extra/clangd/test/Inputs/symbols-modules/module.map
  clang-tools-extra/clangd/test/symbols-modules.test
  clang/include/clang/Lex/Preprocessor.h
  clang/lib/Index/IndexingAction.cpp

Index: clang/lib/Index/IndexingAction.cpp
===================================================================
--- clang/lib/Index/IndexingAction.cpp
+++ clang/lib/Index/IndexingAction.cpp
@@ -147,14 +147,23 @@
   Unit.visitLocalTopLevelDecls(&IndexCtx, topLevelDeclVisitor);
 }
 
-static void indexPreprocessorMacros(const Preprocessor &PP,
+static void indexPreprocessorMacros(Preprocessor &PP,
                                     IndexDataConsumer &DataConsumer) {
-  for (const auto &M : PP.macros())
-    if (MacroDirective *MD = M.second.getLatest())
+  for (const auto &M : PP.macros()) {
+    if (MacroDirective *MD = M.second.getLatest()) {
+      auto *MI = MD->getMacroInfo();
+      if (!MI) {
+        // It is possible for MI to be nullptr if we our translation unit had
+        // only #undef of a macro defined in a module. In that case, dig deeper
+        // and use the definition from the module to yield the same indexing
+        // result whether we are using modules or not.
+        MI = PP.getMacroInfoForLatestDefinition(M.first);
+      }
       DataConsumer.handleMacroOccurrence(
-          M.first, MD->getMacroInfo(),
-          static_cast<unsigned>(index::SymbolRole::Definition),
+          M.first, MI, static_cast<unsigned>(index::SymbolRole::Definition),
           MD->getLocation());
+    }
+  }
 }
 
 void index::indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer,
Index: clang/include/clang/Lex/Preprocessor.h
===================================================================
--- clang/include/clang/Lex/Preprocessor.h
+++ clang/include/clang/Lex/Preprocessor.h
@@ -1115,6 +1115,30 @@
     return nullptr;
   }
 
+  // Returns MacroInfo for the latest definition of II. It could possibly be
+  // imported from a module (thus not accesible via getMacroInfo()), whether
+  // it's active or not.
+  MacroInfo *getMacroInfoForLatestDefinition(const IdentifierInfo *II) {
+    if (!II->hasMacroDefinition())
+      return nullptr;
+    if (auto *MI = getMacroInfo(II))
+      return MI;
+    MacroState &S = CurSubmoduleState->Macros[II];
+    auto ActiveModuleMacros = S.getActiveModuleMacros(*this, II);
+    for (auto MM = ActiveModuleMacros.rbegin(); MM != ActiveModuleMacros.rend();
+         MM++) {
+      if (auto *MI = (*MM)->getMacroInfo())
+        return MI;
+    }
+    auto OverridenMacros = S.getOverriddenMacros();
+    for (auto MM = OverridenMacros.rbegin(); MM != OverridenMacros.rend();
+         MM++) {
+      if (auto *MI = (*MM)->getMacroInfo())
+        return MI;
+    }
+    return nullptr;
+  }
+
   /// Given an identifier, return the latest non-imported macro
   /// directive for that identifier.
   ///
Index: clang-tools-extra/clangd/test/symbols-modules.test
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/test/symbols-modules.test
@@ -0,0 +1,44 @@
+# Verify that we do not crash and correctly find the definition of a macro that
+# was imported from a module and then #undef-ed in preamble.
+#
+# Copy over the test files and fix the paths.
+# RUN: rm -rf %t
+# RUN: cp -r %S/Inputs/symbols-modules %t
+# RUN: sed -i -e "s|DIRECTORY|%t|" %t/compile_commands.json
+#
+# Fix the paths in this file as well. We will pass this copy to clangd.
+# RUN: sed -e "s|DIRECTORY|%/t|g" %s > %t.test.1
+#
+# RUN: clangd -lit-test < %t.test.1 | FileCheck %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"workspace":{"symbol":{"symbolKind":{"valueSet": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}}}},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://DIRECTORY/foo.cpp","languageId":"cpp","version":1,"text":"#include \"bar.h\"\nvoid foo();\n"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"workspace/symbol","params":{"query":"X"}}
+#      CHECK:  "id": 1,
+# CHECK-NEXT:  "jsonrpc": "2.0",
+# CHECK-NEXT:    "result": [
+# CHECK-NEXT:      {
+# CHECK-NEXT:        "containerName": "",
+# CHECK-NEXT:        "kind": 15,
+# CHECK-NEXT:        "location": {
+# CHECK-NEXT:          "range": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": {{.*}},
+# CHECK-NEXT:              "line": {{.*}}
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": {{.*}},
+# CHECK-NEXT:              "line": {{.*}}
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "uri": "file://{{.*}}/foo.h"
+# CHECK-NEXT:        },
+# CHECK-NEXT:        "name": "X"
+# CHECK-NEXT:      }
+# CHECK-NEXT:    ]
+# CHECK-NEXT:}
+---
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
Index: clang-tools-extra/clangd/test/Inputs/symbols-modules/module.map
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/test/Inputs/symbols-modules/module.map
@@ -0,0 +1,4 @@
+module foo {
+  header "foo.h"
+  export *
+}
Index: clang-tools-extra/clangd/test/Inputs/symbols-modules/foo.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/test/Inputs/symbols-modules/foo.h
@@ -0,0 +1 @@
+#define X 1
Index: clang-tools-extra/clangd/test/Inputs/symbols-modules/compile_commands.json
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/test/Inputs/symbols-modules/compile_commands.json
@@ -0,0 +1,5 @@
+[{
+  "directory": "DIRECTORY",
+  "command": "clang -fmodules -Xclang -fmodule-map-file-home-is-cwd -fmodule-map-file=module.map foo.cpp",
+  "file": "DIRECTORY/foo.cpp"
+}]
Index: clang-tools-extra/clangd/test/Inputs/symbols-modules/bar.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/test/Inputs/symbols-modules/bar.h
@@ -0,0 +1,3 @@
+#include "foo.h"
+
+#undef X
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to