You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

317 lines
13 KiB

(*
* Copyright (c) 2015 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
(** Module to preprocess location information in the AST.
The original location information is incremental, each location is a delta
w.r.t. the previous one. This module processes the AST and makes locations explicit. *)
open Utils
module L = Logging
module F = Format
(** Get the sub-declarations of the current declaration. *)
let decl_get_sub_decls decl =
let open Clang_ast_t in
match decl with
| CXXRecordDecl (_, _, _, _, decl_list, _, _, _)
| RecordDecl (_, _, _, _, decl_list, _, _)
| ObjCInterfaceDecl (_, _, decl_list, _, _)
| ObjCProtocolDecl (_, _, decl_list, _, _)
| ObjCCategoryDecl (_, _, decl_list, _, _)
| ObjCCategoryImplDecl (_, _, decl_list, _, _)
| ObjCImplementationDecl (_, _, decl_list, _, _)
| EnumDecl (_, _, _, _, decl_list, _, _)
| LinkageSpecDecl (_, decl_list, _)
| NamespaceDecl (_, _, decl_list, _, _) ->
decl_list
| _ ->
[]
(** Set the sub-declarations of the current declaration. *)
let decl_set_sub_decls decl decl_list' =
let open Clang_ast_t in
match decl with
| CXXRecordDecl (decl_info, name, opt_type, type_ptr, decl_list, decl_context_info, record_decl_info, cxx_record_info) ->
CXXRecordDecl (decl_info, name, opt_type, type_ptr, decl_list', decl_context_info, record_decl_info, cxx_record_info)
| RecordDecl (decl_info, name, opt_type, type_ptr, decl_list, decl_context_info, record_decl_info) ->
RecordDecl (decl_info, name, opt_type, type_ptr, decl_list', decl_context_info, record_decl_info)
| ObjCInterfaceDecl (decl_info, name, decl_list, decl_context_info, obj_c_interface_decl_info) ->
ObjCInterfaceDecl (decl_info, name, decl_list', decl_context_info, obj_c_interface_decl_info)
| ObjCProtocolDecl (decl_info, name, decl_list, decl_context_info, obj_c_protocol_decl_info) ->
ObjCProtocolDecl (decl_info, name, decl_list', decl_context_info, obj_c_protocol_decl_info)
| ObjCCategoryDecl (decl_info, name, decl_list, decl_context_info, category_decl_info) ->
ObjCCategoryDecl (decl_info, name, decl_list', decl_context_info, category_decl_info)
| ObjCCategoryImplDecl (decl_info, name, decl_list, decl_context_info, category_impl_info) ->
ObjCCategoryImplDecl (decl_info, name, decl_list', decl_context_info, category_impl_info)
| ObjCImplementationDecl (decl_info, class_name, decl_list, decl_context_info, idi) ->
ObjCImplementationDecl (decl_info, class_name, decl_list', decl_context_info, idi)
| EnumDecl (decl_info, name, opt_type, type_ptr, decl_list, decl_context_info, enum_decl_info) ->
EnumDecl (decl_info, name, opt_type, type_ptr, decl_list', decl_context_info, enum_decl_info)
| LinkageSpecDecl (decl_info, decl_list, decl_context_info) ->
LinkageSpecDecl (decl_info, decl_list', decl_context_info)
| NamespaceDecl (decl_info, name, decl_list, decl_context_info, namespace_decl_info) ->
NamespaceDecl (decl_info, name, decl_list', decl_context_info, namespace_decl_info)
| _ ->
decl
(** Pretty print a source location. *)
let pp_source_loc fmt source_loc =
let file = match source_loc.Clang_ast_t.sl_file with
| Some file -> file
| None -> "None" in
let line = match source_loc.Clang_ast_t.sl_line with
| Some n -> string_of_int n
| None -> "None" in
let column = match source_loc.Clang_ast_t.sl_column with
| Some n -> string_of_int n
| None -> "None" in
if file = "None" && line = "None" && column = "None"
then F.fprintf fmt "_"
else F.fprintf fmt "%s:%s:%s" file line column
(** Pretty print a source range. *)
let pp_source_range fmt (sloc1, sloc2) =
F.fprintf fmt "<%a, %a>" pp_source_loc sloc1 pp_source_loc sloc2
(** Pretty print an AST. *)
let pp_ast_decl fmt ast_decl =
let rec dump_stmt prefix stmt =
let prefix1 = prefix ^ " " in
let stmt_str = Clang_ast_proj.get_stmt_kind_string stmt in
let stmt_info, stmt_list = Clang_ast_proj.get_stmt_tuple stmt in
let decl_list = match stmt with
| Clang_ast_t.DeclStmt (_, _, decl_list) -> decl_list
| _ -> [] in
F.fprintf fmt "%s%s %a@\n"
prefix
stmt_str
pp_source_range stmt_info.Clang_ast_t.si_source_range;
IList.iter (dump_stmt prefix1) stmt_list;
IList.iter (dump_decl prefix1) decl_list
and dump_decl prefix decl =
let prefix1 = prefix ^ " " in
let open Clang_ast_t in
match decl with
| FunctionDecl (decl_info, name, tp, fdecl_info) ->
F.fprintf fmt "%sFunctionDecl %s %a@\n"
prefix
name.Clang_ast_t.ni_name
pp_source_range decl_info.di_source_range;
IList.iter (dump_decl prefix1) fdecl_info.fdi_decls_in_prototype_scope;
IList.iter (dump_decl prefix1) fdecl_info.fdi_parameters;
Option.may (dump_stmt prefix1) fdecl_info.fdi_body
| ObjCMethodDecl (decl_info, name, obj_c_method_decl_info) ->
F.fprintf fmt "%sObjCMethodDecl %s %a@\n"
prefix
name.Clang_ast_t.ni_name
pp_source_range decl_info.di_source_range;
Option.may (dump_stmt prefix1) obj_c_method_decl_info.omdi_body
| VarDecl (decl_info, name, type_ptr, var_decl_info) ->
F.fprintf fmt "%sVarDecl %a@\n"
prefix
pp_source_range decl_info.di_source_range;
Option.may (dump_stmt prefix1) var_decl_info.vdi_init_expr
| _ ->
let decl_kind_str = Clang_ast_proj.get_decl_kind_string decl in
let decl_info = Clang_ast_proj.get_decl_tuple decl in
let decl_list = decl_get_sub_decls decl in
F.fprintf fmt "%s%s %a@\n"
prefix
decl_kind_str
pp_source_range decl_info.di_source_range;
IList.iter (dump_decl prefix1) decl_list in
let decl_str = Clang_ast_proj.get_decl_kind_string ast_decl in
match ast_decl with
| Clang_ast_t.TranslationUnitDecl (_, decl_list, _, _) ->
F.fprintf fmt "%s (%d declarations)@\n" decl_str (IList.length decl_list);
IList.iter (dump_decl "") decl_list
| _ ->
assert false
(** Compose incremental location information and make locations explicit. *)
module LocComposer : sig
(** Status of the composer. *)
type status
(** Create a new composer with the initial status. *)
val create : unit -> status
(** Compose a new source_range to the current one. *)
val compose : status -> Clang_ast_t.source_range -> Clang_ast_t.source_range
(** Set the current file if specified in the source_range.
The composer will not descend into file included from the current one.
For locations in included files, it will return instead the last known
location of the current file. *)
val set_current_file : status -> Clang_ast_t.source_range -> unit
end = struct
type status =
{ mutable curr_file: string option;
mutable curr_source_range: Clang_ast_t.source_range;
mutable in_curr_file : bool }
let empty_sloc = { Clang_ast_t.sl_file = None; sl_line = None; sl_column = None }
let create () =
{ curr_file = None;
curr_source_range = (empty_sloc, empty_sloc);
in_curr_file = true; }
let set_current_file st (sloc1, sloc2) =
match sloc1.Clang_ast_t.sl_file, sloc2.Clang_ast_t.sl_file with
| _, Some fname
| Some fname, None ->
st.curr_file <- Some fname;
st.in_curr_file <- true
| _ ->
()
let sloc_is_current_file st sloc = match st.curr_file, sloc.Clang_ast_t.sl_file with
| Some curr_f, Some f ->
Some (f = curr_f)
| None, _ -> None
| _, None -> None
let update_sloc st old_sloc new_sloc =
match sloc_is_current_file st new_sloc with
| Some true ->
st.in_curr_file <- true;
new_sloc
| Some false ->
st.in_curr_file <- false;
old_sloc
| None ->
if st.in_curr_file
then
let update x_opt y_opt =
if y_opt <> None then y_opt else x_opt in
let open Clang_ast_t in
{ sl_file = update old_sloc.sl_file new_sloc.sl_file;
sl_line = update old_sloc.sl_line new_sloc.sl_line;
sl_column = update old_sloc.sl_column new_sloc.sl_column }
else
old_sloc
let update_status st (sloc1, sloc2) =
if sloc1 = empty_sloc && sloc2 = empty_sloc
then
()
else
let _, old_sloc2 = st.curr_source_range in
let new_sloc1 = update_sloc st old_sloc2 sloc1 in
let new_sloc2 = update_sloc st new_sloc1 sloc2 in
st.curr_source_range <- (new_sloc1, new_sloc2)
let compose st source_range =
update_status st source_range;
st.curr_source_range
end
let ctor_initializer_process_locs loc_composer ctor_init =
let range' = LocComposer.compose loc_composer ctor_init.Clang_ast_t.xci_source_range in
{ ctor_init with Clang_ast_t.xci_source_range = range'}
(** Apply a location composer to the locations in a statement. *)
let rec stmt_process_locs loc_composer stmt =
let update (stmt_info, stmt_list) =
let range' = LocComposer.compose loc_composer stmt_info.Clang_ast_t.si_source_range in
let stmt_info' =
{ stmt_info with
Clang_ast_t.si_source_range = range' } in
let stmt_list' = IList.map (stmt_process_locs loc_composer) stmt_list in
(stmt_info', stmt_list') in
let open Clang_ast_t in
match Clang_ast_proj.update_stmt_tuple update stmt with
| DeclStmt (stmt_info, stmt_list, decl_list) ->
let decl_list' = IList.map (decl_process_locs loc_composer) decl_list in
DeclStmt (stmt_info, stmt_list, decl_list')
| stmt' ->
stmt'
(** Apply a location composer to the locations in a declaration. *)
and decl_process_locs loc_composer decl =
let decl' =
let update decl_info =
let range' = LocComposer.compose loc_composer decl_info.Clang_ast_t.di_source_range in
{ decl_info with
Clang_ast_t.di_source_range = range' } in
let decl_list = decl_get_sub_decls decl in
let decl1 = Clang_ast_proj.update_decl_tuple update decl in
let decl_list' = IList.map (decl_process_locs loc_composer) decl_list in
decl_set_sub_decls decl1 decl_list' in
let open Clang_ast_t in
let get_updated_fun_decl (decl_info', name, tp, fdecl_info) =
let fdi_decls_in_prototype_scope' =
IList.map (decl_process_locs loc_composer) fdecl_info.fdi_decls_in_prototype_scope in
let fdi_parameters' =
IList.map (decl_process_locs loc_composer) fdecl_info.fdi_parameters in
let body' = Option.map (stmt_process_locs loc_composer) fdecl_info.fdi_body in
let fdecl_info' =
{ fdecl_info with
fdi_body = body';
fdi_parameters = fdi_parameters';
fdi_decls_in_prototype_scope = fdi_decls_in_prototype_scope'; } in
(decl_info', name, tp, fdecl_info') in
let get_updated_method_decl (decl_info', name, tp, fdecl_info, method_info) =
let di', n', tp', fdi' = get_updated_fun_decl (decl_info', name, tp, fdecl_info) in
let ctor_init = method_info.xmdi_cxx_ctor_initializers in
let ctor_init' = IList.map (ctor_initializer_process_locs loc_composer) ctor_init in
let method_info' = { method_info with xmdi_cxx_ctor_initializers = ctor_init'} in
(di', n', tp', fdi', method_info') in
match decl' with
| FunctionDecl fun_info -> FunctionDecl (get_updated_fun_decl fun_info)
| CXXMethodDecl meth_info -> CXXMethodDecl (get_updated_method_decl meth_info)
| CXXConstructorDecl meth_info -> CXXConstructorDecl (get_updated_method_decl meth_info)
| ObjCMethodDecl (decl_info', name, obj_c_method_decl_info) ->
let body' =
Option.map (stmt_process_locs loc_composer) obj_c_method_decl_info.omdi_body in
let obj_c_method_decl_info' = { obj_c_method_decl_info with omdi_body = body' } in
ObjCMethodDecl (decl_info', name, obj_c_method_decl_info')
| VarDecl (decl_info, string, type_ptr, var_decl_info) ->
let vdi_init_expr' =
Option.map (stmt_process_locs loc_composer) var_decl_info.vdi_init_expr in
let var_decl_info' =
{ var_decl_info with vdi_init_expr = vdi_init_expr' } in
VarDecl (decl_info, string, type_ptr, var_decl_info')
| _ ->
decl'
(** Process locations in the AST by making them explicit.
Each toplevel declaration determines the current file,
and once diving into the details of the declaration, location
information about other (include) files is ignored. *)
let ast_decl_process_locs loc_composer ast_decl =
let toplevel_decl_process_locs decl =
let decl_info = Clang_ast_proj.get_decl_tuple decl in
LocComposer.set_current_file loc_composer decl_info.Clang_ast_t.di_source_range;
decl_process_locs loc_composer decl in
match ast_decl with
| Clang_ast_t.TranslationUnitDecl (decl_info, decl_list, decl_context_info, type_list) ->
let decl_list' = IList.map toplevel_decl_process_locs decl_list in
Clang_ast_t.TranslationUnitDecl (decl_info, decl_list', decl_context_info, type_list)
| _ ->
assert false
let preprocess_ast_decl ast_decl =
let loc_composer = LocComposer.create () in
ast_decl_process_locs loc_composer ast_decl