versioner: introduce.

Add a clang-based tool to inspect header availability attributes and
verify them against the NDK platform definitions.

Bug: http://b/28178111
Change-Id: I1bb1925a620e98cc9606cb5a3360b1224c700bd0
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
new file mode 100644
index 0000000..9f02588
--- /dev/null
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DeclarationDatabase.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+
+#include <clang/AST/AST.h>
+#include <clang/AST/Attr.h>
+#include <clang/AST/Mangle.h>
+#include <clang/AST/RecursiveASTVisitor.h>
+#include <clang/Frontend/ASTUnit.h>
+#include <llvm/Support/raw_ostream.h>
+
+using namespace clang;
+
+class Visitor : public RecursiveASTVisitor<Visitor> {
+  HeaderDatabase& database;
+  SourceManager& src_manager;
+  std::unique_ptr<MangleContext> mangler;
+
+ public:
+  Visitor(HeaderDatabase& database, ASTContext& ctx)
+      : database(database), src_manager(ctx.getSourceManager()) {
+    mangler.reset(ItaniumMangleContext::create(ctx, ctx.getDiagnostics()));
+  }
+
+  std::string getDeclName(NamedDecl* decl) {
+    if (auto var_decl = dyn_cast<VarDecl>(decl)) {
+      if (!var_decl->isFileVarDecl()) {
+        return "<local var>";
+      }
+    }
+
+    if (mangler->shouldMangleDeclName(decl)) {
+      std::string mangled;
+      llvm::raw_string_ostream ss(mangled);
+      mangler->mangleName(decl, ss);
+      return mangled;
+    }
+
+    auto identifier = decl->getIdentifier();
+    if (!identifier) {
+      return "<error>";
+    }
+    return identifier->getName();
+  }
+
+  bool VisitDecl(Decl* decl) {
+    // Skip declarations inside of functions (function arguments, variable declarations inside of
+    // inline functions, etc).
+    if (decl->getParentFunctionOrMethod()) {
+      return true;
+    }
+
+    auto named_decl = dyn_cast<NamedDecl>(decl);
+    if (!named_decl) {
+      return true;
+    }
+
+    DeclarationType declaration_type;
+    std::string declaration_name = getDeclName(named_decl);
+    bool is_extern = named_decl->getFormalLinkage() == ExternalLinkage;
+    bool is_definition = false;
+
+    if (auto function_decl = dyn_cast<FunctionDecl>(decl)) {
+      declaration_type = DeclarationType::function;
+      is_definition = function_decl->isThisDeclarationADefinition();
+    } else if (auto var_decl = dyn_cast<VarDecl>(decl)) {
+      if (!var_decl->isFileVarDecl()) {
+        return true;
+      }
+
+      declaration_type = DeclarationType::variable;
+      switch (var_decl->isThisDeclarationADefinition()) {
+        case VarDecl::DeclarationOnly:
+          is_definition = false;
+          break;
+
+        case VarDecl::Definition:
+          is_definition = true;
+          break;
+
+        case VarDecl::TentativeDefinition:
+          // Forbid tentative definitions in headers.
+          fprintf(stderr, "ERROR: declaration '%s' is a tentative definition\n",
+                  declaration_name.c_str());
+          decl->dump();
+          abort();
+      }
+    } else {
+      // We only care about function and variable declarations.
+      return true;
+    }
+
+    if (decl->hasAttr<UnavailableAttr>()) {
+      // Skip declarations that exist only for compile-time diagnostics.
+      return true;
+    }
+
+    // Look for availability annotations.
+    DeclarationAvailability availability;
+    for (const AvailabilityAttr* attr : decl->specific_attrs<AvailabilityAttr>()) {
+      if (attr->getPlatform()->getName() != "android") {
+        fprintf(stderr, "skipping non-android platform %s\n",
+                attr->getPlatform()->getName().str().c_str());
+        continue;
+      }
+      if (attr->getIntroduced().getMajor() != 0) {
+        availability.introduced = attr->getIntroduced().getMajor();
+      }
+      if (attr->getDeprecated().getMajor() != 0) {
+        availability.deprecated = attr->getDeprecated().getMajor();
+      }
+      if (attr->getObsoleted().getMajor() != 0) {
+        availability.obsoleted = attr->getObsoleted().getMajor();
+      }
+    }
+
+    // Find or insert an entry for the declaration.
+    auto declaration_it = database.declarations.find(declaration_name);
+    if (declaration_it == database.declarations.end()) {
+      Declaration declaration = {.name = declaration_name };
+      bool inserted;
+      std::tie(declaration_it, inserted) =
+          database.declarations.insert({ declaration_name, declaration });
+    }
+
+    auto& declaration_locations = declaration_it->second.locations;
+    auto presumed_loc = src_manager.getPresumedLoc(decl->getLocation());
+    DeclarationLocation location = {
+      .filename = presumed_loc.getFilename(),
+      .line_number = presumed_loc.getLine(),
+      .column = presumed_loc.getColumn(),
+      .type = declaration_type,
+      .is_extern = is_extern,
+      .is_definition = is_definition,
+      .availability = availability,
+    };
+
+    // It's fine if the location is already there, we'll get an iterator to the existing element.
+    auto location_it = declaration_locations.begin();
+    bool inserted = false;
+    std::tie(location_it, inserted) = declaration_locations.insert(location);
+
+    // If we didn't insert, check to see if the availability attributes are identical.
+    if (!inserted) {
+      if (location_it->availability != availability) {
+        fprintf(stderr, "ERROR: availability attribute mismatch\n");
+        decl->dump();
+        abort();
+      }
+    }
+
+    return true;
+  }
+};
+
+void HeaderDatabase::parseAST(ASTUnit* ast) {
+  ASTContext& ctx = ast->getASTContext();
+  Visitor visitor(*this, ctx);
+  visitor.TraverseDecl(ctx.getTranslationUnitDecl());
+}