[cfe-commits] r145477 - in /cfe/trunk: include/clang/Basic/DiagnosticFrontendKinds.td include/clang/Frontend/CompilerInstance.h lib/Frontend/CompilerInstance.cpp test/Modules/Inputs/submodules/ test/Modules/Inputs/submodules/module.map test/Modules/Inputs/submodules/type_traits.h test/Modules/Inputs/submodules/vector.h test/Modules/submodules.cpp

Douglas Gregor dgregor at apple.com
Tue Nov 29 20:03:45 PST 2011


Author: dgregor
Date: Tue Nov 29 22:03:44 2011
New Revision: 145477

URL: http://llvm.org/viewvc/llvm-project?rev=145477&view=rev
Log:
When loading a module that involves submodules (e.g., std.vector),
check whether the named submodules themselves are actually
valid, and drill down to the named submodule (although we don't do
anything with it yet). Perform typo correction on the submodule names
when possible.


Added:
    cfe/trunk/test/Modules/Inputs/submodules/
    cfe/trunk/test/Modules/Inputs/submodules/module.map
    cfe/trunk/test/Modules/Inputs/submodules/type_traits.h   (with props)
    cfe/trunk/test/Modules/Inputs/submodules/vector.h   (with props)
    cfe/trunk/test/Modules/submodules.cpp   (with props)
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td
    cfe/trunk/include/clang/Frontend/CompilerInstance.h
    cfe/trunk/lib/Frontend/CompilerInstance.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td?rev=145477&r1=145476&r2=145477&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td Tue Nov 29 22:03:44 2011
@@ -180,4 +180,8 @@
   "no module named '%0' declared in module map file '%1'">, DefaultFatal;
 def err_missing_umbrella_header : Error<
   "cannot open umbrella header '%0': %1">, DefaultFatal;
+def err_no_submodule : Error<"no submodule named %0 in module '%1'">;
+def err_no_submodule_suggest : Error<
+  "no submodule named %0 in module '%1'; did you mean '%2'?">;
+
 }

Modified: cfe/trunk/include/clang/Frontend/CompilerInstance.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CompilerInstance.h?rev=145477&r1=145476&r2=145477&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/CompilerInstance.h (original)
+++ cfe/trunk/include/clang/Frontend/CompilerInstance.h Tue Nov 29 22:03:44 2011
@@ -12,7 +12,9 @@
 
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Lex/ModuleLoader.h"
+#include "clang/Lex/ModuleMap.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/OwningPtr.h"
@@ -34,6 +36,7 @@
 class DiagnosticsEngine;
 class DiagnosticConsumer;
 class ExternalASTSource;
+class FileEntry;
 class FileManager;
 class FrontendAction;
 class Preprocessor;
@@ -96,6 +99,16 @@
   /// \brief Non-owning reference to the ASTReader, if one exists.
   ASTReader *ModuleManager;
 
+  /// \brief A module that we have already attempted to load, which is known
+  /// by either a file entry (FIXME: a temporary measure) or via its module
+  /// definition.
+  typedef llvm::PointerUnion<const FileEntry *, ModuleMap::Module *> 
+    KnownModule;
+  
+  /// \brief The set of top-level modules that has already been loaded,
+  /// along with the module map
+  llvm::DenseMap<const IdentifierInfo *, KnownModule> KnownModules;
+  
   /// \brief Holds information about the output file.
   ///
   /// If TempFilename is not empty we must rename it to Filename at the end.

Modified: cfe/trunk/lib/Frontend/CompilerInstance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInstance.cpp?rev=145477&r1=145476&r2=145477&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInstance.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInstance.cpp Tue Nov 29 22:03:44 2011
@@ -1080,99 +1080,167 @@
 
   StringRef ModuleName = Path[0].first->getName();
   SourceLocation ModuleNameLoc = Path[0].second;
-  
-  // Search for a module with the given name.
-  ModuleMap::Module *Module = 0;
-  std::string ModuleFileName;
-  const FileEntry *ModuleFile
-    = PP->getHeaderSearchInfo().lookupModule(ModuleName, Module,
-                                             &ModuleFileName);
 
-  // FIXME: Verify that the rest of the module path actually corresponds to
-  // a submodule, and pass that information through.
+  ModuleMap::Module *Module = 0;
+  const FileEntry *ModuleFile = 0;
   
-  bool BuildingModule = false;
-  if (!ModuleFile && Module) {
-    // The module is not cached, but we have a module map from which we can
-    // build the module.
-
-    // Check whether there is a cycle in the module graph.
-    SmallVectorImpl<std::string> &ModuleBuildPath
-      = getPreprocessorOpts().ModuleBuildPath;
-    SmallVectorImpl<std::string>::iterator Pos
-      = std::find(ModuleBuildPath.begin(), ModuleBuildPath.end(), ModuleName);
-    if (Pos != ModuleBuildPath.end()) {
-      llvm::SmallString<256> CyclePath;
-      for (; Pos != ModuleBuildPath.end(); ++Pos) {
-        CyclePath += *Pos;
-        CyclePath += " -> ";
+  // If we don't already have information on this module, load the module now.
+  KnownModule &Known = KnownModules[Path[0].first];
+  if (Known.isNull()) {  
+    // Search for a module with the given name.
+    std::string ModuleFileName;
+    ModuleFile
+      = PP->getHeaderSearchInfo().lookupModule(ModuleName, Module,
+                                               &ModuleFileName);
+
+    bool BuildingModule = false;
+    if (!ModuleFile && Module) {
+      // The module is not cached, but we have a module map from which we can
+      // build the module.
+
+      // Check whether there is a cycle in the module graph.
+      SmallVectorImpl<std::string> &ModuleBuildPath
+        = getPreprocessorOpts().ModuleBuildPath;
+      SmallVectorImpl<std::string>::iterator Pos
+        = std::find(ModuleBuildPath.begin(), ModuleBuildPath.end(), ModuleName);
+      if (Pos != ModuleBuildPath.end()) {
+        llvm::SmallString<256> CyclePath;
+        for (; Pos != ModuleBuildPath.end(); ++Pos) {
+          CyclePath += *Pos;
+          CyclePath += " -> ";
+        }
+        CyclePath += ModuleName;
+
+        getDiagnostics().Report(ModuleNameLoc, diag::err_module_cycle)
+          << ModuleName << CyclePath;
+        return 0;
       }
-      CyclePath += ModuleName;
 
-      getDiagnostics().Report(ModuleNameLoc, diag::err_module_cycle)
-        << ModuleName << CyclePath;
+      getDiagnostics().Report(ModuleNameLoc, diag::warn_module_build)
+        << ModuleName;
+      BuildingModule = true;
+      compileModule(*this, Module, ModuleFileName);
+      ModuleFile = FileMgr->getFile(ModuleFileName);
+    }
+
+    if (!ModuleFile) {
+      getDiagnostics().Report(ModuleNameLoc,
+                              BuildingModule? diag::err_module_not_built
+                                            : diag::err_module_not_found)
+        << ModuleName
+        << SourceRange(ImportLoc, ModuleNameLoc);
       return 0;
     }
 
-    getDiagnostics().Report(ModuleNameLoc, diag::warn_module_build)
-      << ModuleName;
-    BuildingModule = true;
-    compileModule(*this, Module, ModuleFileName);
-    ModuleFile = FileMgr->getFile(ModuleFileName);
-  }
+    // If we don't already have an ASTReader, create one now.
+    if (!ModuleManager) {
+      if (!hasASTContext())
+        createASTContext();
+
+      std::string Sysroot = getHeaderSearchOpts().Sysroot;
+      const PreprocessorOptions &PPOpts = getPreprocessorOpts();
+      ModuleManager = new ASTReader(getPreprocessor(), *Context,
+                                    Sysroot.empty() ? "" : Sysroot.c_str(),
+                                    PPOpts.DisablePCHValidation,
+                                    PPOpts.DisableStatCache);
+      if (hasASTConsumer()) {
+        ModuleManager->setDeserializationListener(
+          getASTConsumer().GetASTDeserializationListener());
+        getASTContext().setASTMutationListener(
+          getASTConsumer().GetASTMutationListener());
+      }
+      llvm::OwningPtr<ExternalASTSource> Source;
+      Source.reset(ModuleManager);
+      getASTContext().setExternalSource(Source);
+      if (hasSema())
+        ModuleManager->InitializeSema(getSema());
+      if (hasASTConsumer())
+        ModuleManager->StartTranslationUnit(&getASTConsumer());
+    }
 
-  if (!ModuleFile) {
-    getDiagnostics().Report(ModuleNameLoc,
-                            BuildingModule? diag::err_module_not_built
-                                          : diag::err_module_not_found)
-      << ModuleName
-      << SourceRange(ImportLoc, ModuleNameLoc);
-    return 0;
-  }
+    // Try to load the module we found.
+    switch (ModuleManager->ReadAST(ModuleFile->getName(),
+                                   serialization::MK_Module)) {
+    case ASTReader::Success:
+      break;
+
+    case ASTReader::IgnorePCH:
+      // FIXME: The ASTReader will already have complained, but can we showhorn
+      // that diagnostic information into a more useful form?
+      return 0;
 
-  // If we don't already have an ASTReader, create one now.
-  if (!ModuleManager) {
-    if (!hasASTContext())
-      createASTContext();
-
-    std::string Sysroot = getHeaderSearchOpts().Sysroot;
-    const PreprocessorOptions &PPOpts = getPreprocessorOpts();
-    ModuleManager = new ASTReader(getPreprocessor(), *Context,
-                                  Sysroot.empty() ? "" : Sysroot.c_str(),
-                                  PPOpts.DisablePCHValidation,
-                                  PPOpts.DisableStatCache);
-    if (hasASTConsumer()) {
-      ModuleManager->setDeserializationListener(
-        getASTConsumer().GetASTDeserializationListener());
-      getASTContext().setASTMutationListener(
-        getASTConsumer().GetASTMutationListener());
+    case ASTReader::Failure:
+      // Already complained.
+      return 0;
     }
-    llvm::OwningPtr<ExternalASTSource> Source;
-    Source.reset(ModuleManager);
-    getASTContext().setExternalSource(Source);
-    if (hasSema())
-      ModuleManager->InitializeSema(getSema());
-    if (hasASTConsumer())
-      ModuleManager->StartTranslationUnit(&getASTConsumer());
+    
+    if (Module)
+      Known = Module;
+    else
+      Known = ModuleFile;
+  } else {
+    Module = Known.dyn_cast<ModuleMap::Module *>();
   }
-
-  // Try to load the module we found.
-  switch (ModuleManager->ReadAST(ModuleFile->getName(),
-                                 serialization::MK_Module)) {
-  case ASTReader::Success:
-    break;
-
-  case ASTReader::IgnorePCH:
-    // FIXME: The ASTReader will already have complained, but can we showhorn
-    // that diagnostic information into a more useful form?
-    return 0;
-
-  case ASTReader::Failure:
-    // Already complained.
-    return 0;
+  
+  // Verify that the rest of the module path actually corresponds to
+  // a submodule.
+  ModuleMap::Module *Sub = 0;
+  if (Module && Path.size() > 1) {
+    Sub = Module;
+    for (unsigned I = 1, N = Path.size(); I != N; ++I) {
+      StringRef Name = Path[I].first->getName();
+      llvm::StringMap<ModuleMap::Module *>::iterator Pos
+        = Sub->SubModules.find(Name);
+      
+      if (Pos == Sub->SubModules.end()) {
+        // Attempt to perform typo correction to find a module name that works.
+        llvm::SmallVector<StringRef, 2> Best;
+        unsigned BestEditDistance = (std::numeric_limits<unsigned>::max)();
+        
+        for (llvm::StringMap<ModuleMap::Module *>::iterator 
+                  I = Sub->SubModules.begin(), 
+               IEnd = Sub->SubModules.end();
+             I != IEnd; ++I) {
+          unsigned ED = Name.edit_distance(I->getValue()->Name,
+                                           /*AllowReplacements=*/true,
+                                           BestEditDistance);
+          if (ED <= BestEditDistance) {
+            if (ED < BestEditDistance)
+              Best.clear();
+            Best.push_back(I->getValue()->Name);
+          }
+        }
+        
+        // If there was a clear winner, user it.
+        if (Best.size() == 1) {
+          getDiagnostics().Report(Path[I].second, 
+                                  diag::err_no_submodule_suggest)
+            << Path[I].first << Sub->getFullModuleName() << Best[0]
+            << SourceRange(Path[0].second, Path[I-1].second)
+            << FixItHint::CreateReplacement(SourceRange(Path[I].second),
+                                            Best[0]);
+          Pos = Sub->SubModules.find(Best[0]);
+        }
+      }
+      
+      if (Pos == Sub->SubModules.end()) {
+        // No submodule by this name. Complain, and don't look for further
+        // submodules.
+        getDiagnostics().Report(Path[I].second, diag::err_no_submodule)
+          << Path[I].first << Sub->getFullModuleName()
+          << SourceRange(Path[0].second, Path[I-1].second);
+        break;
+      }
+      
+      Sub = Pos->getValue();
+    }
   }
-
-  // FIXME: The module file's FileEntry makes a poor key indeed!
-  return (ModuleKey)ModuleFile;
+  
+  // FIXME: Tell the AST reader to make the named submodule visible.
+  
+  // FIXME: The module file's FileEntry makes a poor key indeed! Once we 
+  // eliminate the need for FileEntry here, the module itself will become the
+  // key (which does make sense).
+  return Known.getOpaqueValue();
 }
 

Added: cfe/trunk/test/Modules/Inputs/submodules/module.map
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/submodules/module.map?rev=145477&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/submodules/module.map (added)
+++ cfe/trunk/test/Modules/Inputs/submodules/module.map Tue Nov 29 22:03:44 2011
@@ -0,0 +1,4 @@
+module std {
+  module vector { header "vector.h" }
+  module type_traits { header "type_traits.h" }
+}

Added: cfe/trunk/test/Modules/Inputs/submodules/type_traits.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/submodules/type_traits.h?rev=145477&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/submodules/type_traits.h (added)
+++ cfe/trunk/test/Modules/Inputs/submodules/type_traits.h Tue Nov 29 22:03:44 2011
@@ -0,0 +1,9 @@
+template<typename T>
+struct remove_reference {
+  typedef T type;
+};
+
+template<typename T>
+struct remove_reference<T&> {
+  typedef T type;
+};

Propchange: cfe/trunk/test/Modules/Inputs/submodules/type_traits.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/Modules/Inputs/submodules/type_traits.h
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/Modules/Inputs/submodules/type_traits.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cfe/trunk/test/Modules/Inputs/submodules/vector.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/submodules/vector.h?rev=145477&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/submodules/vector.h (added)
+++ cfe/trunk/test/Modules/Inputs/submodules/vector.h Tue Nov 29 22:03:44 2011
@@ -0,0 +1 @@
+template<typename T> class vector;

Propchange: cfe/trunk/test/Modules/Inputs/submodules/vector.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/Modules/Inputs/submodules/vector.h
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/Modules/Inputs/submodules/vector.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cfe/trunk/test/Modules/submodules.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/submodules.cpp?rev=145477&view=auto
==============================================================================
--- cfe/trunk/test/Modules/submodules.cpp (added)
+++ cfe/trunk/test/Modules/submodules.cpp Tue Nov 29 22:03:44 2011
@@ -0,0 +1,6 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodule-cache-path %t -fauto-module-import -I %S/Inputs/submodules %s -verify
+
+__import_module__ std.vector;
+__import_module__ std.typetraits; // expected-error{{no submodule named 'typetraits' in module 'std'; did you mean 'type_traits'?}}
+__import_module__ std.vector.compare; // expected-error{{no submodule named 'compare' in module 'std.vector'}}

Propchange: cfe/trunk/test/Modules/submodules.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/Modules/submodules.cpp
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/Modules/submodules.cpp
------------------------------------------------------------------------------
    svn:mime-type = text/plain





More information about the cfe-commits mailing list